提供彈性的小工具版面配置

試試 Compose 方式
Jetpack Compose 是 Android 推薦的 UI 工具包。瞭解如何使用 Compose 樣式的 API 建構小工具。

本頁說明 Android 12 (API 級別 31) 導入的小工具大小調整功能,以及更靈活的調整方式。此外,本文也會詳細說明如何判斷小工具的大小

使用改良版 API 取得小工具大小和版面配置

從 Android 12 (API 級別 31) 開始,您可以透過下列方式提供更精細的大小屬性和彈性版面配置,詳情請參閱後續章節:

  1. 指定其他小工具大小限制。

  2. 提供回應式版面配置確切版面配置

在舊版 Android 中,可以使用 OPTION_APPWIDGET_MIN_WIDTHOPTION_APPWIDGET_MIN_HEIGHTOPTION_APPWIDGET_MAX_WIDTHOPTION_APPWIDGET_MAX_HEIGHT 額外資訊取得小工具的大小範圍,然後估算小工具的大小,但這種邏輯不適用於所有情況。如果小工具指定 Android 12 以上版本,建議提供回應式精確版面配置

指定其他小工具大小限制

Android 12 新增的 API 可確保小工具在不同螢幕尺寸的裝置上,都能更可靠地調整大小。

除了現有的 minWidthminHeightminResizeWidthminResizeHeight 屬性,請使用下列新的 appwidget-provider 屬性:

以下 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) 到 160 dp × 199 dp (下一個分界點 - 1 dp)。
  • tallView 支援 160dp × 200dp 到 214dp (下一個截斷點 - 1) × 200dp。
  • wideView支援從 215dp × 110dp (minResizeHeight) 到 250dp (maxResizeWidth) × 200dp (maxResizeHeight)。

小工具必須支援 minResizeWidth × minResizeHeightmaxResizeWidth × maxResizeHeight 的大小範圍。您可以在該範圍內決定切換版面的分界點。

回覆式版面配置範例
圖 1 回應式版面配置範例。

提供確切的版面配置

如果無法提供一小組回應式版面配置,您可以改為提供針對小工具顯示大小量身打造的不同版面配置。通常手機會有兩種尺寸 (直向和橫向模式),摺疊式裝置則有四種尺寸。

如要實作這項解決方案,應用程式必須執行下列步驟:

  1. 覆寫 AppWidgetProvider.onAppWidgetOptionsChanged(),當大小組合變更時呼叫。

  2. 呼叫 AppWidgetManager.getAppWidgetOptions(),這會傳回包含大小的 Bundle

  3. Bundle 存取 AppWidgetManager.OPTION_APPWIDGET_SIZES 鍵。

以下程式碼範例說明如何提供確切的版面配置。

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

決定小工具的大小

每個小工具都必須為搭載 Android 12 以上版本的裝置定義 targetCellWidthtargetCellHeight,或是為所有 Android 版本定義 minWidthminHeight,指出小工具預設占用的最小空間量。不過,使用者將小工具加到主畫面時,小工具通常會佔用超過您指定的最小寬度和高度。

Android 主畫面提供可用空間的格線,使用者可以在格線上放置小工具和圖示。這個格線會因裝置而異,舉例來說,許多手機提供 5x4 格線,平板電腦則提供較大的格線。新增小工具時,系統會將其延展至佔用最少數量的儲存格,以滿足 Android 12 以上版本裝置的 targetCellWidthtargetCellHeight 限制,或是 Android 11 (API 級別 30) 以下版本裝置的 minWidthminHeight 限制。

儲存格的寬度和高度,以及套用至小工具的自動邊界大小,可能會因裝置而異。請參閱下表,根據您想佔用的格線儲存格數量,大致估算一般 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)

請根據直向模式的儲存格大小,為 minWidthminResizeWidthmaxResizeWidth 屬性提供值。同樣地,請使用橫向模式的儲存格大小,為 minHeightminResizeHeightmaxResizeHeight 屬性提供值。

這是因為直向模式下的儲存格寬度通常比橫向模式小,同樣地,橫向模式下的儲存格高度通常比直向模式小。

舉例來說,如果希望小工具的寬度可縮小至 Google Pixel 4 上的一個儲存格,請將 minResizeWidth 設為最多 56dp,確保 minResizeWidth 屬性的值小於 57dp,因為直向模式的儲存格寬度至少為 57dp。同樣地,如果想在同一裝置上,讓小工具的高度可在一個儲存格中調整大小,請將 minResizeHeight 設為最多 50dp,確保 minResizeHeight 屬性的值小於 51dp,因為在橫向模式中,一個儲存格的高度至少為 51dp。

每個小工具的大小都可調整,範圍介於 minResizeWidth/minResizeHeightmaxResizeWidth/maxResizeHeight 屬性之間,因此必須能適應這兩個屬性之間的任何大小範圍。

舉例來說,如要設定小工具在刊登位置上的預設大小,可以設定下列屬性:

<appwidget-provider
    android:targetCellWidth="3"
    android:targetCellHeight="2"
    android:minWidth="180dp"
    android:minHeight="110dp">
</appwidget-provider>

也就是說,小工具的預設大小為 3x2 個儲存格,如 targetCellWidthtargetCellHeight 屬性所指定;如果是搭載 Android 11 以下版本的裝置,則為 180×110dp,如 minWidthminHeight 所指定。在後者中,儲存格大小可能會因裝置而異。

此外,如要設定小工具支援的大小範圍,可以設定下列屬性:

<appwidget-provider
    android:minResizeWidth="180dp"
    android:minResizeHeight="110dp"
    android:maxResizeWidth="530dp"
    android:maxResizeHeight="450dp">
</appwidget-provider>

如上述屬性所指定,小工具的寬度可從 180dp 調整為 530dp,高度則可從 110dp 調整為 450dp。只要符合下列條件,小工具就能從 3x2 格調整為 5x2 格:

  • 裝置有 5x4 格線。
  • 儲存格數量與可用大小 (以 dp 為單位) 之間的對應關係,請參閱本頁的表格,瞭解最小尺寸的估算值
  • 小工具會配合該尺寸範圍調整大小。

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

假設小工具使用上述程式碼片段中定義的動態版面配置。也就是說,從 180dp (minResizeWidth) x 110dp (minResizeHeight) 到 269x279dp (下一個分界點 - 1),都會使用指定為 R.layout.widget_weather_forecast_small 的版面配置。同樣地, R.layout.widget_weather_forecast_medium 的使用範圍為 270x110dp 至 270x279dp, 而 R.layout.widget_weather_forecast_large 的使用範圍為 270x280dp 至 530dp (maxResizeWidth) x 450dp (maxResizeHeight)。

使用者調整小工具大小時,小工具的外觀會隨之改變,以配合每個大小的儲存格,如下列範例所示。

最小 3x2 格線大小的天氣小工具範例。使用者介面會顯示地點名稱 (東京)、溫度 (14°) 和局部多雲天氣的符號。
圖 2. 3x2 R.layout.widget_weather_forecast_small

4x2「中」尺寸的天氣小工具範例。以這種方式調整小工具大小時,會以先前小工具大小的所有 UI 為基礎,並新增「多雲」標籤和下午 4 點到 7 點的溫度預報。
圖 3 4x2 R.layout.widget_weather_forecast_medium

5x2「中」尺寸的天氣小工具範例。以這種方式調整小工具大小,會產生與先前大小相同的使用者介面,但會延伸一個儲存格長度,佔用更多水平空間。
圖 4:5x2 R.layout.widget_weather_forecast_medium

5x3「大型」尺寸的天氣小工具範例。以這種方式調整小工具大小時,會以先前小工具大小的所有 UI 為基礎,並在小工具內新增檢視畫面,其中包含週二和週三的天氣預報。顯示晴天或雨天等天氣的符號,以及每日最高和最低溫度。
圖 5 5x3 R.layout.widget_weather_forecast_large.

5x4「大型」尺寸的天氣小工具範例。以這種方式調整小工具大小時,會以先前小工具大小的所有 UI 為基礎,並新增週四和週五 (以及對應的符號,指出每天的天氣類型和高低溫)。
圖 6:5x4 R.layout.widget_weather_forecast_large