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

ลองใช้ 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
  ...
  android:targetCellWidth="3"
  android:targetCellHeight="2"
  android:maxResizeWidth="250dp"
  android:maxResizeHeight="110dp">
</appwidget-provider>

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

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

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

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

Kotlin

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

    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)
}

Java

@Override
public void onUpdate(...) {
    RemoteViews smallView = ...;
    RemoteViews tallView = ...;
    RemoteViews wideView = ...;

    Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>();
    viewMapping.put(new SizeF(150f, 100f), smallView);
    viewMapping.put(new SizeF(150f, 200f), tallView);
    viewMapping.put(new SizeF(215f, 100f), wideView);
    RemoteViews remoteViews = new RemoteViews(viewMapping);

    appWidgetManager.updateAppWidget(id, remoteViews);
}

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

<appwidget-provider
    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

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

Kotlin

override fun onAppWidgetOptionsChanged(
        context: Context,
        appWidgetManager: AppWidgetManager,
        id: Int,
        newOptions: Bundle?
) {
    super.onAppWidgetOptionsChanged(context, appWidgetManager, id, newOptions)
    // Get the new sizes.
    val sizes = newOptions?.getParcelableArrayList<SizeF>(
            AppWidgetManager.OPTION_APPWIDGET_SIZES
    )
    // 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 { }

Java

@Override
public void onAppWidgetOptionsChanged(
    Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
    super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
    // Get the new sizes.
    ArrayList<SizeF> sizes =
        newOptions.getParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES);
    // Check that the list of sizes is provided by the launcher.
    if (sizes == null || sizes.isEmpty()) {
      return;
    }
    // Map the sizes to the RemoteViews that you want.
    Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>();
    for (SizeF size : sizes) {
        viewMapping.put(size, createRemoteViews(size));
    }
    RemoteViews remoteViews = new RemoteViews(viewMapping);
    appWidgetManager.updateAppWidget(id, remoteViews);
}

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

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

วิดเจ็ตแต่ละรายการต้องกําหนด 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
    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
    android:minResizeWidth="180dp"
    android:minResizeHeight="110dp"
    android:maxResizeWidth="530dp"
    android:maxResizeHeight="450dp">
</appwidget-provider>

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

Kotlin

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))

Java

RemoteViews smallView = 
    new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_small);
RemoteViews mediumView = 
    new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_medium);
RemoteViews largeView = 
    new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_large);

Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>();
viewMapping.put(new SizeF(180f, 110f), smallView);
viewMapping.put(new SizeF(270f, 110f), mediumView);
viewMapping.put(new SizeF(270f, 280f), largeView);
RemoteViews remoteViews = new RemoteViews(viewMapping);

appWidgetManager.updateAppWidget(id, remoteViews);

สมมติว่าวิดเจ็ตใช้เลย์เอาต์ที่ตอบสนองที่กำหนดไว้ในข้อมูลโค้ดก่อนหน้า ซึ่งหมายความว่าเลย์เอาต์ที่ระบุเป็น 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.