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

หน้านี้จะอธิบายการปรับแต่งขนาดวิดเจ็ตและความยืดหยุ่นที่มากขึ้นซึ่งเปิดตัวใน 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. Overload 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

ตัวอย่างวิดเจ็ตสภาพอากาศขนาด 4x2 &quot;กลาง&quot; การปรับขนาดวิดเจ็ตด้วยวิธีนี้จะสร้าง 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