มีเลย์เอาต์วิดเจ็ตที่ยืดหยุ่น

ลองใช้วิธีแบบ Compose
Jetpack Compose เป็นชุดเครื่องมือ UI ที่แนะนำสำหรับ Android ดูวิธีสร้างวิดเจ็ตโดยใช้ API รูปแบบ Compose

หน้านี้อธิบายการปรับแต่งขนาดวิดเจ็ตและความยืดหยุ่นที่มากขึ้น ซึ่งเปิดตัวใน Android 12 (API ระดับ 31) นอกจากนี้ยังให้รายละเอียดเกี่ยวกับวิธี กำหนดขนาดสำหรับวิดเจ็ตด้วย

ใช้ API ที่ปรับปรุงแล้วสำหรับขนาดและเลย์เอาต์วิดเจ็ต

ตั้งแต่ Android 12 (ระดับ API 31) เป็นต้นไป คุณจะระบุแอตทริบิวต์ขนาดที่ละเอียดยิ่งขึ้นและเลย์เอาต์ที่ยืดหยุ่นได้โดยทำดังนี้ ตามที่อธิบายไว้ในส่วนต่างๆ ที่จะกล่าวถึงต่อไป

  1. ระบุข้อจำกัดการปรับขนาดวิดเจ็ตเพิ่มเติม

  2. การระบุเลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์หรือเลย์เอาต์ที่แน่นอน

ใน Android เวอร์ชันก่อนหน้า คุณสามารถรับช่วงขนาดของวิดเจ็ตได้โดยใช้ส่วนเสริม OPTION_APPWIDGET_MIN_WIDTH, OPTION_APPWIDGET_MIN_HEIGHT, OPTION_APPWIDGET_MAX_WIDTH และ OPTION_APPWIDGET_MAX_HEIGHT จากนั้นจึงประมาณขนาดของวิดเจ็ต แต่ตรรกะดังกล่าวใช้ไม่ได้ในทุกสถานการณ์ สำหรับวิดเจ็ตที่กำหนดเป้าหมายเป็น Android 12 ขึ้นไป เราขอแนะนำให้ระบุเลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์หรือเลย์เอาต์ที่แน่นอน

ระบุข้อจำกัดเพิ่มเติมในการปรับขนาดวิดเจ็ต

Android 12 เพิ่ม API ที่ช่วยให้คุณมั่นใจได้ว่าวิดเจ็ตจะมีขนาดที่เชื่อถือได้มากขึ้นในอุปกรณ์ต่างๆ ที่มีขนาดหน้าจอแตกต่างกัน

นอกเหนือจากแอตทริบิวต์ minWidth, minHeight, minResizeWidth, และ minResizeHeight ที่มีอยู่แล้ว ให้ใช้แอตทริบิวต์ appwidget-provider ใหม่ต่อไปนี้

  • targetCellWidth และ targetCellHeight กำหนดขนาดเป้าหมายของวิดเจ็ตในแง่ของเซลล์ตารางกริดของ Launcher หากมีการกำหนด ระบบจะใช้แอตทริบิวต์เหล่านี้แทน minWidth หรือ minHeight

  • maxResizeWidth และ maxResizeHeight: กำหนดขนาดสูงสุดที่ตัวเรียกใช้จะอนุญาต ให้ผู้ใช้ปรับขนาดวิดเจ็ต

XML ต่อไปนี้แสดงวิธีใช้แอตทริบิวต์การปรับขนาด

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
  android:targetCellWidth="3"
  android:targetCellHeight="2"
  android:maxResizeWidth="250dp"
  android:maxResizeHeight="110dp">
</appwidget-provider>

ระบุเลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์

หากจำเป็นต้องเปลี่ยนเลย์เอาต์ตามขนาดของวิดเจ็ต เราขอแนะนำให้ สร้างชุดเลย์เอาต์ขนาดเล็ก โดยแต่ละเลย์เอาต์จะใช้ได้กับขนาดที่หลากหลาย หากทำไม่ได้ อีกทางเลือกหนึ่งคือการระบุเลย์เอาต์ตามขนาดวิดเจ็ตที่แน่นอนขณะรันไทม์ตามที่อธิบายไว้ในหน้านี้

ฟีเจอร์นี้ช่วยให้การปรับขนาดราบรื่นขึ้นและระบบโดยรวมมีประสิทธิภาพดีขึ้น เนื่องจากระบบไม่ต้องปลุกแอปทุกครั้ง ที่แสดงวิดเจ็ตในขนาดที่ต่างกัน

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีระบุรายการเลย์เอาต์

override fun onUpdate(
    /* ... */
) {
    val smallView = RemoteViews(
        /* ... */
    )
    val tallView = RemoteViews(
        /* ... */
    )
    val wideView = RemoteViews(
        /* ... */
    )

    val viewMapping: Map<SizeF, RemoteViews> = mapOf(
        SizeF(150f, 100f) to smallView,
        SizeF(150f, 200f) to tallView,
        SizeF(215f, 100f) to wideView
    )
    val remoteViews = RemoteViews(viewMapping)

    appWidgetManager.updateAppWidget(id, remoteViews)
}

สมมติว่าวิดเจ็ตมีแอตทริบิวต์ต่อไปนี้

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minResizeWidth="160dp"
    android:minResizeHeight="110dp"
    android:maxResizeWidth="250dp"
    android:maxResizeHeight="200dp">
</appwidget-provider>

ข้อมูลโค้ดข้างต้นมีความหมายดังนี้

  • smallView รองรับตั้งแต่ 160dp (minResizeWidth) × 110dp (minResizeHeight) ไปจนถึง 160dp × 199dp (จุดตัดถัดไป - 1dp)
  • tallView รองรับตั้งแต่ 160dp × 200dp ถึง 214dp (จุดตัดถัดไป - 1) × 200dp
  • wideView รองรับตั้งแต่ 215dp × 110dp (minResizeHeight) ไปจนถึง 250dp (maxResizeWidth) × 200dp (maxResizeHeight)

วิดเจ็ตต้องรองรับช่วงขนาดตั้งแต่ minResizeWidth × minResizeHeight ถึง maxResizeWidth × maxResizeHeight ภายในช่วงนั้น คุณสามารถกำหนดจุดตัดเพื่อสลับเลย์เอาต์ได้

ตัวอย่างเลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์
รูปที่ 1 ตัวอย่างเลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์

ระบุเลย์เอาต์ที่แน่นอน

หากไม่สามารถใช้เลย์เอาต์ที่ปรับเปลี่ยนตามพื้นที่โฆษณาจำนวนเล็กน้อยได้ คุณสามารถระบุเลย์เอาต์ต่างๆ ที่ปรับให้เหมาะกับขนาดที่วิดเจ็ตแสดงแทนได้ โดยปกติแล้วจะมี 2 ขนาดสำหรับโทรศัพท์ (โหมดแนวตั้งและโหมดแนวนอน) และ 4 ขนาดสำหรับอุปกรณ์พับได้

หากต้องการใช้โซลูชันนี้ แอปของคุณต้องทำตามขั้นตอนต่อไปนี้

  1. โอเวอร์โหลด AppWidgetProvider.onAppWidgetOptionsChanged(), ซึ่งจะเรียกใช้เมื่อชุดขนาดมีการเปลี่ยนแปลง

  2. เรียก AppWidgetManager.getAppWidgetOptions() ซึ่งจะแสดงผล Bundle ที่มีขนาด

  3. เข้าถึงคีย์ AppWidgetManager.OPTION_APPWIDGET_SIZES จาก Bundle

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีระบุเลย์เอาต์ที่แน่นอน

override fun onAppWidgetOptionsChanged(
    context: Context,
    appWidgetManager: AppWidgetManager,
    id: Int,
    newOptions: Bundle?
) {
    super.onAppWidgetOptionsChanged(context, appWidgetManager, id, newOptions)
    // Get the new sizes.
    val sizes = newOptions?.let {
        BundleCompat.getParcelableArrayList(
            it,
            AppWidgetManager.OPTION_APPWIDGET_SIZES,
            SizeF::class.java
        )
    }
    // Check that the list of sizes is provided by the launcher.
    if (sizes.isNullOrEmpty()) {
        return
    }
    // Map the sizes to the RemoteViews that you want.
    val remoteViews = RemoteViews(sizes.associateWith(::createRemoteViews))
    appWidgetManager.updateAppWidget(id, remoteViews)
}

// Create the RemoteViews for the given size.
private fun createRemoteViews(size: SizeF): RemoteViews {
}

กำหนดขนาดวิดเจ็ต

วิดเจ็ตแต่ละรายการต้องกำหนด targetCellWidth และ targetCellHeight สำหรับอุปกรณ์ที่ใช้ Android 12 ขึ้นไป หรือ minWidth และ minHeight สำหรับ Android ทุกเวอร์ชัน ซึ่งระบุจำนวนพื้นที่ขั้นต่ำที่วิดเจ็ตใช้โดยค่าเริ่มต้น อย่างไรก็ตาม เมื่อผู้ใช้เพิ่มวิดเจ็ตลงในหน้าจอหลัก โดยทั่วไปแล้ววิดเจ็ตจะใช้ความกว้างและความสูงมากกว่าค่าต่ำสุดที่คุณระบุ

หน้าจอหลักของ Android จะแสดงตารางพื้นที่ว่างให้ผู้ใช้ วางวิดเจ็ตและไอคอน ตารางกริดนี้อาจแตกต่างกันไปตามอุปกรณ์ เช่น โทรศัพท์มือถือหลายรุ่นมีตารางกริด 5x4 และแท็บเล็ตอาจมีตารางกริดที่ใหญ่กว่า เมื่อเพิ่มวิดเจ็ต ระบบจะขยายวิดเจ็ตให้ครอบคลุมจำนวนเซลล์ขั้นต่ำ ในแนวนอนและแนวตั้งตามที่จำเป็นเพื่อให้เป็นไปตามข้อจำกัดสำหรับ targetCellWidth และ targetCellHeight ในอุปกรณ์ที่ใช้ Android 12 ขึ้นไป หรือข้อจำกัด minWidth และ minHeight ในอุปกรณ์ที่ใช้ Android 11 (ระดับ API 30) หรือต่ำกว่า

ขนาดเซลล์และระยะขอบอัตโนมัติจะแตกต่างกันไปในแต่ละอุปกรณ์ ใช้ตารางต่อไปนี้ เพื่อประมาณขนาดขั้นต่ำของวิดเจ็ตในตารางกริด 5x4 ทั่วไป ของโทรศัพท์ โดยพิจารณาจากจำนวนเซลล์กริดที่ต้องการใช้

จำนวนเซลล์ (กว้าง x สูง) ขนาดที่ใช้ได้ในโหมดแนวตั้ง (dp) ขนาดที่ใช้ได้ในโหมดแนวนอน (dp)
1x1 57x102dp 127x51dp
2x1 130x102dp 269x51dp
3x1 203x102dp 412x51dp
4x1 276x102dp 554x51dp
5x1 349x102dp 697x51dp
5x2 349x220dp 697x117dp
5x3 349x337dp 697x184dp
5x4 349x455dp 697x250dp
... ... ...
n x m (73n - 16) x (118m - 16) (142n - 15) x (66m - 15)

ใช้ขนาดเซลล์โหมดแนวตั้งเพื่อแจ้งค่าที่คุณระบุสำหรับแอตทริบิวต์ minWidth, minResizeWidth และ maxResizeWidth ในทำนองเดียวกัน ให้ใช้ขนาดเซลล์ในโหมดแนวนอนเพื่อกำหนดค่าที่คุณระบุ สำหรับแอตทริบิวต์ minHeight, minResizeHeight และ maxResizeHeight

สาเหตุคือโดยปกติแล้วความกว้างของเซลล์ในโหมดแนวตั้งจะน้อยกว่าในโหมดแนวนอน และในทำนองเดียวกัน ความสูงของเซลล์ในโหมดแนวนอนจะน้อยกว่าในโหมดแนวตั้ง

เช่น หากต้องการให้ความกว้างของวิดเจ็ตปรับขนาดลงได้ถึง 1 เซลล์ใน Google Pixel 4 คุณต้องตั้งค่า minResizeWidth ไม่เกิน 56dp เพื่อให้แน่ใจว่าค่าของแอตทริบิวต์ minResizeWidth มีขนาดเล็กกว่า 57dp เนื่องจากเซลล์มีความกว้างอย่างน้อย 57dp ในแนวตั้ง ในทำนองเดียวกัน หากต้องการให้ปรับความสูงของวิดเจ็ตในเซลล์เดียวบนอุปกรณ์เดียวกันได้ คุณต้องตั้งค่า minResizeHeight ไม่เกิน 50dp เพื่อให้แน่ใจว่าค่าของแอตทริบิวต์ minResizeHeight จะน้อยกว่า 51dp เนื่องจากเซลล์เดียวมีความสูงอย่างน้อย 51dp ในโหมดแนวนอน

วิดเจ็ตแต่ละรายการสามารถปรับขนาดได้ภายในช่วงขนาดระหว่างแอตทริบิวต์ minResizeWidth/minResizeHeight และ maxResizeWidth/maxResizeHeight ซึ่งหมายความว่าวิดเจ็ตต้องปรับให้เข้ากับช่วงขนาดใดก็ได้ระหว่างแอตทริบิวต์ดังกล่าว

ตัวอย่างเช่น หากต้องการตั้งค่าขนาดเริ่มต้นของวิดเจ็ตในตําแหน่ง คุณสามารถ ตั้งค่าแอตทริบิวต์ต่อไปนี้

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:targetCellWidth="3"
    android:targetCellHeight="2"
    android:minWidth="180dp"
    android:minHeight="110dp">
</appwidget-provider>

ซึ่งหมายความว่าขนาดเริ่มต้นของวิดเจ็ตคือ 3x2 เซลล์ ตามที่ระบุโดยแอตทริบิวต์ targetCellWidth และ targetCellHeight หรือ 180×110dp ตามที่ระบุโดย minWidth และ minHeight สำหรับอุปกรณ์ที่ใช้ Android 11 หรือต่ำกว่า ในกรณีหลัง ขนาดในเซลล์อาจ แตกต่างกันไปตามอุปกรณ์

นอกจากนี้ หากต้องการตั้งค่าช่วงขนาดที่รองรับของวิดเจ็ต คุณสามารถตั้งค่าแอตทริบิวต์ต่อไปนี้ได้

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minResizeWidth="180dp"
    android:minResizeHeight="110dp"
    android:maxResizeWidth="530dp"
    android:maxResizeHeight="450dp">
</appwidget-provider>

ตามที่ระบุไว้ในแอตทริบิวต์ก่อนหน้า ความกว้างของวิดเจ็ตจะ ปรับขนาดได้ตั้งแต่ 180dp ถึง 530dp และความสูงจะปรับขนาดได้ตั้งแต่ 110dp ถึง 450dp จากนั้นจะปรับขนาดวิดเจ็ตจาก 3x2 เป็น 5x2 เซลล์ได้ตราบใดที่มีเงื่อนไขต่อไปนี้

val smallView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_small)
val mediumView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_medium)
val largeView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_large)

val viewMapping: Map<SizeF, RemoteViews> = mapOf(
    SizeF(180f, 110f) to smallView,
    SizeF(270f, 110f) to mediumView,
    SizeF(270f, 280f) to largeView
)

appWidgetManager.updateAppWidget(appWidgetId, RemoteViews(viewMapping))

สมมติว่าวิดเจ็ตใช้เลย์เอาต์ที่ตอบสนองที่กำหนดไว้ในข้อมูลโค้ดก่อนหน้า ซึ่งหมายความว่าเลย์เอาต์ที่ระบุเป็น R.layout.widget_weather_forecast_small จะใช้ตั้งแต่ 180dp (minResizeWidth) x 110dp (minResizeHeight) ไปจนถึง 269x279dp (จุดตัดถัดไป - 1) ในทำนองเดียวกัน R.layout.widget_weather_forecast_medium จะใช้ตั้งแต่ 270x110dp ถึง 270x279dp และ R.layout.widget_weather_forecast_large จะใช้ตั้งแต่ 270x280dp ถึง 530dp (maxResizeWidth) x 450dp (maxResizeHeight)

เมื่อผู้ใช้ปรับขนาดวิดเจ็ต ลักษณะที่ปรากฏของวิดเจ็ตจะเปลี่ยนไปเพื่อให้เหมาะกับแต่ละขนาดใน เซลล์ ดังที่แสดงในตัวอย่างต่อไปนี้

ตัวอย่างวิดเจ็ตสภาพอากาศในขนาดกริด 3x2 ที่เล็กที่สุด UI แสดง
            ชื่อสถานที่ (โตเกียว) อุณหภูมิ (14°) และสัญลักษณ์ที่บ่งบอกถึง
            สภาพอากาศที่มีเมฆเป็นบางส่วน
รูปที่ 2 3x2 R.layout.widget_weather_forecast_small

ตัวอย่างวิดเจ็ตสภาพอากาศขนาด &quot;ปานกลาง&quot; 4x2 การปรับขนาดวิดเจ็ต
            ด้วยวิธีนี้จะสร้างขึ้นจาก UI ทั้งหมดจากขนาดวิดเจ็ตก่อนหน้า
            และเพิ่มป้ายกำกับ &quot;มีเมฆเป็นส่วนใหญ่&quot; รวมถึงพยากรณ์อุณหภูมิตั้งแต่
            16:00 น. ถึง 19:00 น.
รูปที่ 3 4x2 R.layout.widget_weather_forecast_medium

ตัวอย่างวิดเจ็ตสภาพอากาศในขนาด &quot;ปานกลาง&quot; 5x2 การปรับขนาดวิดเจ็ต
            ด้วยวิธีนี้จะทำให้ UI เหมือนกับขนาดก่อนหน้า เพียงแต่จะ
            ขยายออกไป 1 เซลล์เพื่อใช้พื้นที่แนวนอนมากขึ้น
รูปที่ 4 5x2 R.layout.widget_weather_forecast_medium.

ตัวอย่างวิดเจ็ตสภาพอากาศในขนาด &quot;ใหญ่&quot; 5x3 การปรับขนาดวิดเจ็ต
            ด้วยวิธีนี้จะสร้างขึ้นจาก UI ทั้งหมดจากขนาดวิดเจ็ตก่อนหน้า
            และเพิ่มมุมมองภายในวิดเจ็ตที่มีการพยากรณ์อากาศ
            ในวันอังคารและวันพุธ สัญลักษณ์ที่ระบุสภาพอากาศที่มีแดดหรือฝนตก
            และอุณหภูมิสูงสุดและต่ำสุดของแต่ละวัน
รูปที่ 5 5x3 R.layout.widget_weather_forecast_large.

ตัวอย่างวิดเจ็ตสภาพอากาศในขนาด &quot;ใหญ่&quot; 5x4 การปรับขนาดวิดเจ็ต
            ด้วยวิธีนี้จะต่อยอดจาก UI ทั้งหมดจากขนาดวิดเจ็ตก่อนหน้า
            และเพิ่มวันพฤหัสบดีและวันศุกร์ (และสัญลักษณ์ที่เกี่ยวข้อง
            ซึ่งระบุประเภทสภาพอากาศ รวมถึงอุณหภูมิสูงสุดและต่ำสุด
            ของแต่ละวัน)
รูปที่ 6 5x4 R.layout.widget_weather_forecast_large