透過 Glance 建立應用程式小工具

以下各節說明如何使用 Glance 建立基本應用程式小工具。

在資訊清單中宣告 AppWidget

完成設定步驟後,請在應用程式中宣告 AppWidget 及其中繼資料。

  1. 從 GlanceAppWidgetReceiver 擴充 AppWidget 接收器:

    class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
        override val glanceAppWidget: GlanceAppWidget = TODO("Create GlanceAppWidget")
    }

  2. 在 AndroidManifest.xml 檔案和相關聯的中繼資料檔案中,註冊應用程式小工具的供應商:

        <receiver android:name=".glance.MyReceiver"
        android:exported="true">
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>
        <meta-data
            android:name="android.appwidget.provider"
            android:resource="@xml/my_app_widget_info" />
    </receiver>
    

新增 AppWidgetProviderInfo 中繼資料

接著,請按照「建立小工具」指南,在 @xml/my_app_widget_info 檔案中建立及定義應用程式小工具資訊。

Glance 唯一的差別在於沒有 initialLayout XML,但您必須定義一個。您可以使用程式庫提供的預先定義載入版面配置:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/glance_default_loading_layout">
</appwidget-provider>

宣告 AppWidgetProviderInfo XML

AppWidgetProviderInfo 物件會定義小工具的基本特質。在 XML 中繼資料資源檔案 (res/xml/my_app_widget_info.xml) 的 <appwidget-provider> 元素中定義 AppWidgetProviderInfo:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="40dp"
    android:minHeight="40dp"
    android:targetCellWidth="1"
    android:targetCellHeight="1"
    android:maxResizeWidth="250dp"
    android:maxResizeHeight="120dp"
    android:updatePeriodMillis="86400000"
    android:description="@string/example_appwidget_description"
    android:previewLayout="@layout/example_appwidget_preview"
    android:initialLayout="@layout/glance_default_loading_layout"
    android:configure="com.example.android.ExampleAppWidgetConfigurationActivity"
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen"
    android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>

小工具大小屬性

預設主畫面會根據已定義高度和寬度的儲存格格線,在視窗中放置小工具。大多數主畫面只允許小工具採用格線儲存格整數倍的大小,例如水平兩個儲存格、垂直三個儲存格。

小工具大小屬性可讓您指定小工具的預設大小,並提供小工具大小的下限和上限。在這種情況下,小工具的預設大小是指小工具首次新增至主畫面時的大小。

下表說明與小工具大小相關的 <appwidget-provider> 屬性:

屬性和說明
targetCellWidth 和 targetCellHeight (Android 12)、minWidth 和 minHeight
  • 從 Android 12 開始,targetCellWidth 和 targetCellHeight 屬性會以格線儲存格指定小工具的預設大小。在 Android 11 以下版本中,系統會忽略這些屬性,如果主畫面不支援網格版面配置,系統可以忽略這些屬性。
  • minWidth 和 minHeight 屬性會以 dp 為單位指定小工具的預設大小。如果小工具的最小寬度或高度值與儲存格尺寸不符,系統會將這些值無條件進位至最接近的儲存格大小。
建議您同時指定兩組屬性:targetCellWidth 和 targetCellHeight,以及 minWidth 和 minHeight,這樣一來,如果使用者的裝置不支援 targetCellWidth 和 targetCellHeight,應用程式就能改用 minWidth 和 minHeight。如果支援,targetCellWidth 和 targetCellHeight 屬性的優先順序會高於 minWidth 和 minHeight 屬性。
minResizeWidth和minResizeHeight 指定小工具的絕對最小尺寸。這些值會指定小工具無法辨識或無法使用的尺寸。使用這些屬性可讓使用者將小工具調整為小於預設小工具大小的大小。如果 minResizeWidth 屬性大於 minWidth,或未啟用水平調整大小功能,系統會忽略該屬性。請參閱 resizeMode。同樣地,如果 minResizeHeight 屬性大於 minHeight,或未啟用垂直調整大小功能,系統就會忽略該屬性。
maxResizeWidth和maxResizeHeight 指定小工具的建議大小上限。如果值不是格線儲存格維度的倍數,則會無條件進位至最接近的儲存格大小。如果 maxResizeWidth 屬性小於 minWidth,或未啟用水平調整大小功能,系統會忽略該屬性。請參閱 resizeMode。同樣地,如果 maxResizeHeight 屬性小於 minHeight,或未啟用垂直調整大小功能,系統就會忽略該屬性。Android 12 推出。
resizeMode 指定小工具的大小調整規則。您可以使用這項屬性,讓主畫面小工具可水平、垂直或同時在兩個軸上調整大小。使用者只要按住小工具,就會顯示大小調整控點,接著拖曳水平或垂直控點,即可在版面配置格線上變更大小。resizeMode 屬性的值包括 horizontal、vertical 和 none。如要將小工具宣告為可水平和垂直調整大小,請使用 horizontal|vertical。

範例

為說明上表中的屬性如何影響小工具大小,請假設以下規格:

  • 格狀儲存格寬 30 dp,高 50 dp。
  • 以下是屬性規格:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="80dp"
    android:minHeight="80dp"
    android:targetCellWidth="2"
    android:targetCellHeight="2"
    android:minResizeWidth="40dp"
    android:minResizeHeight="40dp"
    android:maxResizeWidth="120dp"
    android:maxResizeHeight="120dp"
    android:resizeMode="horizontal|vertical" />

Android 12 以上版本:

使用 targetCellWidth 和 targetCellHeight 屬性做為小工具的預設大小。

小工具的預設大小為 2x2。小工具的大小可縮小至 2x1,或放大至 4x3。

Android 11 以下版本:

使用 minWidth 和 minHeight 屬性,計算小工具的預設大小。

預設寬度 = Math.ceil(80 / 30) = 3

預設高度 = Math.ceil(80 / 50) = 2

小工具的預設大小為 3x2。小工具的大小可以縮小至 2x1,或放大至全螢幕。

其他小工具屬性

下表說明與小工具大小以外品質相關的 <appwidget-provider> 屬性。

屬性和說明
updatePeriodMillis 定義小工具架構透過呼叫 onUpdate() 回呼方法,向 GlanceAppWidgetReceiver 要求更新的頻率。建議盡可能降低更新頻率 (每小時不超過一次),以節省電池電力。詳情請參閱「Glance 狀態管理」中的「何時更新小工具」一節。
initialLayout 指向版面配置資源,定義 Glance UI 組合轉譯前的小工具載入版面配置。您可以使用程式庫中提供的預先定義載入版面配置:@layout/glance_default_loading_layout。
configure 定義使用者新增小工具時啟動的設定活動。請參閱「協助使用者設定應用程式小工具」指南。
description 指定小工具挑選器要顯示的小工具說明。Android 12 推出。
previewLayout (Android 12) 和 previewImage (Android 11 以下版本)
  • 從 Android 12 開始,previewLayout 屬性會指定可調整大小的預覽畫面,您可將其設為小工具預設大小的 XML 版面配置。理想情況下,這會指向與設計版面配置相符的靜態 XML 對應。
  • 在 Android 11 以下版本中,previewImage 屬性會指定小工具外觀的靜態圖片可繪螢幕截圖,顯示在小工具挑選器中。
建議您同時指定這兩項屬性,讓應用程式在舊版平台上能順利改用備用字型。如果是較新的平台 (Android 15 以上版本),您可以使用 `GlanceAppWidget.providePreview` 在 Kotlin 中定義即時產生的預覽畫面。請參閱產生的預覽畫面指南。
autoAdvanceViewId 指定小工具主機自動跳轉的小工具子檢視畫面檢視 ID。
widgetCategory 宣告小工具是否可顯示在主畫面 (home_screen)、螢幕鎖定畫面 (keyguard) 或兩者。如果是 Android 5.0 以上版本,則只有 home_screen 有效。
widgetFeatures 宣告小工具支援的功能。舉例來說,如果小工具的設定為選用,請同時指定 configuration_optional 和 reconfigurable。

定義「GlanceAppWidget」

  1. 建立新的類別,從 GlanceAppWidget 延伸並覆寫 provideGlance 方法。您可以使用這個方法載入轉譯小工具所需的資料:

    class MyAppWidget : GlanceAppWidget() {
    
        override suspend fun provideGlance(context: Context, id: GlanceId) {
    
            // In this method, load data needed to render the AppWidget.
            // Use `withContext` to switch to another thread for long running
            // operations.
    
            provideContent {
                // create your AppWidget here
                Text("Hello World")
            }
        }
    }

  2. 在 GlanceAppWidgetReceiver 的 glanceAppWidget 中例項化:

    class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
    
        // Let MyAppWidgetReceiver know which GlanceAppWidget to use
        override val glanceAppWidget: GlanceAppWidget = MyAppWidget()
    }

您已使用 Glance 設定 AppWidget。

管理不同大小值區的小工具圖片記憶體

如果小工具顯示點陣圖或 content:// 圖片 URI,請按照下列步驟操作,將序列化 RemoteViews 圖片記憶體總量控制在系統限制內 (6 * width * height 位元組,在 Android 17 以上版本中由 IllegalArgumentException 強制執行):

  • 在 provideContent 之前解碼共用點陣圖:如果小工具使用 SizeMode.Responsive 或 SizeMode.Exact,Glance 會為每個 DpSize 值區執行一次 provideContent lambda。在 provideContent 內解碼 Bitmap 會為每個值區分配獨立的 Bitmap 執行個體,使 RemoteViews.mBitmapCache 中的記憶體用量倍增。在 provideContent 之前,先在 provideGlance 內解碼降取樣點陣圖 (不透明圖片為 Bitmap.Config.RGB_565),讓所有值區共用單一 Bitmap 參照。
  • 使用相符的 ImageProvider 匯入:Jetpack Glance 在兩個套件中提供兩種不同的 ImageProvider 超載:

    • 傳遞記憶體內 ImageProvider(bitmap: Bitmap) 或可繪製資源 ImageProvider(resId: Int) 時,請使用 import androidx.glance.ImageProvider。
    • 傳遞 URI ImageProvider(uri: Uri) 時,請使用 import androidx.glance.appwidget.ImageProvider。透過 FileProvider URI 提供快取檔案時,請將 Intent.FLAG_GRANT_READ_URI_PERMISSION 授予每個已安裝的 Intent.CATEGORY_HOME 啟動器套件,讓主機可以轉譯圖片。

使用 GlanceAppWidgetReceiver 類別處理小工具廣播

GlanceAppWidgetReceiver 座標小工具會擴充基礎 AppWidgetProvider,藉此廣播和更新平台狀態。當小工具更新、刪除、啟用或停用時,它會接收平台事件,並將其轉譯為 Compose 生命週期要求。

在資訊清單中宣告小工具

在 AndroidManifest.xml 檔案中,將 GlanceAppWidgetReceiver 類別子類別宣告為廣播接收器:

<receiver android:name="MyReceiver"
          android:exported="false">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider"
               android:resource="@xml/my_app_widget_info" />
</receiver>

<receiver> 元素需要 android:name 屬性,用於指定接收器類別。接收器必須接受 <intent-filter> 內的 ACTION_APPWIDGET_UPDATE 廣播動作。

<meta-data> 元素必須將其名稱識別為 android.appwidget.provider,且 android:resource 屬性必須指向 AppWidgetProviderInfo XML 中繼資料資源 (@xml/my_app_widget_info)。

實作 GlanceAppWidgetReceiver 類別

在 Glance 中,您會擴充 GlanceAppWidgetReceiver,而不是直接擴充 AppWidgetProvider。如要實作,請將接收器連結至 GlanceAppWidget 執行個體。GlanceAppWidgetReceiver 中可用的主要回呼運作方式如下:

  • onUpdate():由 Glance 自動覆寫,以執行組合結構更新。如果您手動覆寫 onUpdate,則必須呼叫 super.onUpdate,Glance 才能成功啟動組合執行緒。
  • onAppWidgetOptionsChanged():在首次放置小工具或調整大小時呼叫。Glance 會在幕後讀取選項套件項目,因此版面配置會根據執行階段維度順暢調整。
  • onDeleted(Context, IntArray):每當使用者刪除特定小工具例項時,系統就會叫用這個方法。
  • onEnabled(Context):成功建立第一個小工具例項時觸發。非常適合執行全域遷移作業。
  • onDisabled(Context):在移除供應器的最後一個有效執行個體時呼叫。
  • onReceive(Context, Intent):在特定回呼方法之前,攔截每個平台廣播。您必須確保編寫的任何自訂接收器邏輯都會呼叫 super.onReceive(context, intent),且絕不能自行呼叫 goAsync,因為 Glance 會自動以非同步方式轉送工作。

接收小工具廣播意圖

在幕後,GlanceAppWidgetReceiver 會篩選並處理下列基礎平台小工具廣播意圖:

建立 UI

下列程式碼片段示範如何建立 UI:

/* Import Glance Composables
 In the event there is a name clash with the Compose classes of the same name,
 you may rename the imports per https://kotlinlang.org/docs/packages.html#imports
 using the `as` keyword.

import androidx.glance.Button
import androidx.glance.layout.Column
import androidx.glance.layout.Row
import androidx.glance.text.Text
*/
class MyAppWidget : GlanceAppWidget() {

    override suspend fun provideGlance(context: Context, id: GlanceId) {
        // Load data needed to render the AppWidget.
        // Use `withContext` to switch to another thread for long running
        // operations.

        provideContent {
            // create your AppWidget here
            MyContent()
        }
    }

    @Composable
    private fun MyContent() {
        Column(
            modifier = GlanceModifier.fillMaxSize(),
            verticalAlignment = Alignment.Top,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp))
            Row(horizontalAlignment = Alignment.CenterHorizontally) {
                Button(
                    text = "Home",
                    onClick = actionStartActivity<MyActivity>()
                )
                Button(
                    text = "Work",
                    onClick = actionStartActivity<MyActivity>()
                )
            }
        }
    }
}

上述程式碼範例會執行下列作業:

  • 在頂層 Column 中,項目會依序垂直排列。
  • Column 會透過 GlanceModifier 展開大小,以符合可用空間,並將內容對齊頂端 (verticalAlignment) 並水平置中 (horizontalAlignment)。
  • Column 的內容是使用 lambda 定義。順序很重要。
    • Column 中的第一個項目是 Text 元件,內邊距為 12.dp。
    • 第二個項目是 Row,其中項目會依序水平排列,並有兩個 Buttons 水平置中 (horizontalAlignment)。最終顯示效果取決於可用空間。以下是可能顯示的畫面範例:
destination_widget
圖 1. UI 範例。

您可以變更對齊值,或套用不同的修飾符值 (例如邊框間距),變更元件的位置和大小。如需各類別的完整元件、參數和可用修飾符清單,請參閱參考文件。

實作圓角

Android 12 導入系統參數,可動態自訂應用程式小工具的圓角半徑:

  • system_app_widget_background_radius:指定小工具背景容器的圓角半徑 (不得大於 28 dp)。
  • 內半徑:為避免內容遭到裁剪,請根據系統背景外框,計算內部內容的比例半徑:systemRadiusValue - widgetPadding

在資訊一覽中,您可以使用 GlanceModifier.cornerRadius(android.R.dimen.system_app_widget_background_radius),在組合中動態套用圓角半徑大小屬性。

如要在搭載 Android 11 (API 級別 30) 以下版本的裝置上提供回溯相容性,請實作自訂屬性和自訂主題資源備用方案:

  • /values/attrs.xml

    <resources>
    <attr name="backgroundRadius" format="dimension" />
    </resources>
    
  • /values/styles.xml

    <resources>
    <style name="MyWidgetTheme">
      <item name="backgroundRadius">@dimen/my_background_radius_dimen</item>
    </style>
    </resources>
    
  • /values-31/styles.xml

    <resources>
    <style name="MyWidgetTheme" parent="@android:style/Theme.DeviceDefault.DayNight">
      <item name="backgroundRadius">@android:dimen/system_app_widget_background_radius</item>
    </style>
    </resources>
    
  • /drawable/my_widget_background.xml

    <shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="?attr/backgroundRadius" />
    </shape>