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

หน้านี้อธิบายการปรับแต่งเพื่อปรับขนาดวิดเจ็ตและความยืดหยุ่นที่มากขึ้น เปิดตัวใน 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 รองรับตั้งแต่ 160 dp (minResizeWidth) × 110 dp (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
... ... ...
กว้าง x ม. (73n - 16) x (118 นาที - 16) (142n - 15) x (66 นาที - 15)

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

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

ตัวอย่างเช่น หากคุณต้องการปรับขนาดความกว้างของวิดเจ็ตให้เล็กลงเหลือเพียงเซลล์เดียว 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 ถึง 530 dp (maxResizeWidth) x 450 dp (maxResizeHeight)

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

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

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

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

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

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