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

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

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

ตัวอย่างวิดเจ็ตสภาพอากาศในขนาด &#39;ปานกลาง&#39; 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.