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

ลองใช้วิธีแบบ 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: กำหนดขนาดสูงสุดที่ Launcher อนุญาตให้ผู้ใช้ปรับขนาดวิดเจ็ตได้

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. Overload 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 ในแนวตั้ง ในทำนองเดียวกัน หากต้องการให้ความสูงของวิดเจ็ตปรับขนาดได้ใน 1 เซลล์บนอุปกรณ์เดียวกัน คุณต้องตั้งค่า minResizeHeight เป็น 50dp เป็นอย่างมาก เพื่อให้แน่ใจว่าค่าของแอตทริบิวต์ minResizeHeight น้อยกว่า 51dp เนื่องจาก 1 เซลล์มีความสูงอย่างน้อย 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