應用程式小工具是小型的應用程式檢視畫面,可以嵌入其他應用程式 (例如主畫面),並定期接收更新。這類檢視畫面在使用者介面中稱為「小工具」,而您可以透過應用程式小工具供應商 (或「小工具供應商」) 發布小工具。存放其他小工具的應用程式元件稱為應用程式小工具主機 (或「小工具主機」)。圖 1 是音樂小工具範例:
本文件說明如何使用小工具供應器發布小工具。如要進一步瞭解如何建立自己的 AppWidgetHost
來代管應用程式小工具,請參閱「建構小工具主機」。
如要瞭解如何設計小工具,請參閱「應用程式小工具總覽」。
小工具元件
如要建立小工具,您需要下列基本元件:
AppWidgetProviderInfo
物件- 說明小工具的中繼資料,例如小工具的版面配置、更新頻率和
AppWidgetProvider
類別。AppWidgetProviderInfo
是在 XML 中定義,如本文件所述。 AppWidgetProvider
類別- 定義基本方法,透過小工具以程式輔助方式介面。透過這項功能,當小工具更新、啟用、停用或刪除時,您就會收到廣播訊息。您在資訊清單中宣告
AppWidgetProvider
,然後按照本文件的說明實作該變數。 - 查看版面配置
- 定義小工具的初始版面配置。如本文件所述,以 XML 定義版面配置。
圖 2 顯示這些元件如何融入整體應用程式小工具處理流程。
如果小工具需要使用者設定,請實作應用程式小工具設定活動。這個活動可讓使用者修改小工具設定,例如時鐘小工具的時區。
- 從 Android 12 (API 級別 31) 開始,您可以提供預設設定,讓使用者稍後重新設定小工具。詳情請參閱「使用小工具的預設設定」和「允許使用者重新設定放置的小工具」。
- 在 Android 11 (API 級別 30) 以下版本中,每當使用者將小工具新增至主畫面時,就會啟動此活動。
我們也建議您採用以下改善項目:彈性小工具版面配置、其他強化功能、進階小工具、集合小工具,以及建構小工具主機。
宣告 AppWidgetProviderInfo XML
AppWidgetProviderInfo
物件會定義小工具的基本特質。使用單一 <appwidget-provider>
元素在 XML 資源檔案中定義 AppWidgetProviderInfo
物件,並將其儲存至專案的 res/xml/
資料夾。
例如:
<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/example_loading_appwidget"
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 |
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() 回呼方法,要求小工具架構向 AppWidgetProvider 要求更新的頻率。我們無法保證實際更新會依照此值準確即時更新,為節省電池電力,建議您盡可能避免頻繁更新 (最多每小時一次)。如需選擇適當更新週期的完整注意事項清單,請參閱「小工具內容更新最佳化」。 |
initialLayout |
指向定義小工具版面配置的版面配置資源。 |
configure |
定義使用者新增小工具時啟動的活動,讓小工具設定小工具屬性。請參閱「允許使用者設定小工具」。從 Android 12 開始,應用程式可以略過初始設定。詳情請參閱使用小工具的預設設定。 |
description |
指定小工具挑選器的說明,讓小工具顯示。相關元素已在 Android 12 中推出。 |
previewLayout (Android 12) 和 previewImage (Android 11 以下版本) |
previewImage 和 previewLayout 屬性,這樣一來,如果使用者的裝置不支援 previewLayout ,應用程式仍可改回使用 previewImage 。詳情請參閱「與可擴充的小工具預覽功能回溯相容性」。 |
autoAdvanceViewId |
指定由小工具主機自動進階的小工具子檢視畫面的檢視 ID。 |
widgetCategory |
宣告小工具是否能顯示在主畫面 (home_screen )、螢幕鎖定畫面 (keyguard ) 或兩者並用。如果是 Android 5.0 以上版本,只有 home_screen 有效。 |
widgetFeatures |
宣告小工具支援的功能。舉例來說,如果您想讓小工具在使用者新增時採用預設設定,請同時指定 configuration_optional 和 reconfigurable 標記。這樣就不必在使用者新增小工具後啟動設定活動。使用者之後仍可重新設定小工具。 |
使用 AppWidgetProvider 類別處理小工具廣播訊息
AppWidgetProvider
類別會處理小工具廣播訊息,並更新小工具來回應小工俱生命週期事件。以下各節說明如何在資訊清單中宣告 AppWidgetProvider
並進行實作。
在資訊清單中宣告小工具
首先,請在應用程式的 AndroidManifest.xml
檔案中宣告 AppWidgetProvider
類別,如以下範例所示:
<receiver android:name="ExampleAppWidgetProvider"
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/example_appwidget_info" />
</receiver>
<receiver>
元素需要 android:name
屬性,用於指定小工具使用的 AppWidgetProvider
。除非需要向 AppWidgetProvider
廣播其他程序 (通常為例外),否則請勿匯出元件。
<intent-filter>
元素必須包含含有 android:name
屬性的 <action>
元素。這個屬性指定 AppWidgetProvider
接受 ACTION_APPWIDGET_UPDATE
廣播。這是唯一必須明確宣告的廣播訊息。AppWidgetManager
會視需要自動將所有其他小工具廣播訊息傳送到 AppWidgetProvider
。
<meta-data>
元素會指定 AppWidgetProviderInfo
資源,且需要下列屬性:
android:name
:指定中繼資料名稱。使用android.appwidget.provider
,將資料視為AppWidgetProviderInfo
描述元。android:resource
:指定AppWidgetProviderInfo
資源位置。
實作 AppWidgetProvider 類別
AppWidgetProvider
類別會擴充 BroadcastReceiver
做為處理小工具廣播的便利類別。只會接收與小工具相關的事件廣播,例如小工具的更新、刪除、啟用和停用時。當這些廣播事件發生時,系統會呼叫下列 AppWidgetProvider
方法:
onUpdate()
- 呼叫此方法,依照
AppWidgetProviderInfo
中updatePeriodMillis
屬性所定義的間隔更新小工具。詳情請參閱本頁的說明其他小工具屬性的表格。 - 當使用者新增小工具時,系統也會呼叫此方法,以便執行基本設定,例如定義
View
物件的事件處理常式或啟動工作以載入要在小工具中顯示的資料。不過,如果您在沒有configuration_optional
旗標的情況下宣告設定活動,則當使用者新增小工具時,系統「不會」呼叫這個方法,但系統「會」呼叫此方法來進行後續更新。設定活動的責任在設定完成後執行第一次更新。詳情請參閱「允許使用者設定應用程式小工具」。 - 最重要的回呼是
onUpdate()
。詳情請參閱本頁的「使用onUpdate()
類別處理事件」。 onAppWidgetOptionsChanged()
系統會在首次放置小工具及每次調整小工具大小時呼叫此方法。使用這個回呼,即可根據小工具的大小範圍顯示或隱藏內容。取得大小範圍 (自 Android 12 起,小工具執行個體可採用的可能尺寸清單) 是透過呼叫
getAppWidgetOptions()
來傳回Bundle
,其中包含以下內容:OPTION_APPWIDGET_MIN_WIDTH
:包含小工具執行個體的寬度下限 (以 dp 單位為單位)。OPTION_APPWIDGET_MIN_HEIGHT
:包含小工具執行個體的高度下限 (以 dp 為單位)。OPTION_APPWIDGET_MAX_WIDTH
:包含小工具執行個體的寬度上限 (以 dp 單位為單位)。OPTION_APPWIDGET_MAX_HEIGHT
:包含小工具執行個體的高度上限 (以 dp 為單位)。OPTION_APPWIDGET_SIZES
:包含小工具執行個體所能可能的大小清單 (List<SizeF>
),以 dp 為單位。相關元素已在 Android 12 中推出。
onDeleted(Context, int[])
每次從小工具主機刪除小工具時,系統就會呼叫此方法。
onEnabled(Context)
系統會在首次建立小工具例項時呼叫此方法。舉例來說,如果使用者在小工具中加入兩個例項,系統只會第一次呼叫此方法。如果您需要開啟新的資料庫,或執行只需針對所有小工具執行個體執行一次的其他設定,就很適合使用此選項。
onDisabled(Context)
從小工具主機刪除小工具的最後例項時,系統就會呼叫此方法。您可以在這裡清除
onEnabled(Context)
中完成的所有工作,例如刪除臨時資料庫。onReceive(Context, Intent)
系統會針對每個廣播訊息和上述各個回呼方法之前呼叫這個方法。您通常不需要實作這個方法,因為預設的
AppWidgetProvider
實作會篩除所有小工具廣播,並視情況呼叫上述方法。
您必須使用 AndroidManifest
中的 <receiver>
元素,將 AppWidgetProvider
類別實作宣告為廣播接收器。詳情請參閱本頁的「在資訊清單中宣告小工具」。
使用 onUpdate() 類別處理事件
最重要的 AppWidgetProvider
回呼為 onUpdate()
,因為除非您使用沒有 configuration_optional
旗標的設定活動,否則系統會在每個小工具新增至主機時呼叫此回呼。如果小工具接受任何使用者互動事件,請在這個回呼中註冊事件處理常式。如果小工具不會建立暫存檔案或資料庫,或是執行其他需要清除的工作,那麼 onUpdate()
可能是您唯一需要定義的回呼方法。
舉例來說,如果您想讓小工具包含輕觸時啟動活動的按鈕,可以使用以下 AppWidgetProvider
實作方式:
Kotlin
class ExampleAppWidgetProvider : AppWidgetProvider() { override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray ) { // Perform this loop procedure for each widget that belongs to this // provider. appWidgetIds.forEach { appWidgetId -> // Create an Intent to launch ExampleActivity. val pendingIntent: PendingIntent = PendingIntent.getActivity( /* context = */ context, /* requestCode = */ 0, /* intent = */ Intent(context, ExampleActivity::class.java), /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) // Get the layout for the widget and attach an onClick listener to // the button. val views: RemoteViews = RemoteViews( context.packageName, R.layout.appwidget_provider_layout ).apply { setOnClickPendingIntent(R.id.button, pendingIntent) } // Tell the AppWidgetManager to perform an update on the current // widget. appWidgetManager.updateAppWidget(appWidgetId, views) } } }
Java
public class ExampleAppWidgetProvider extends AppWidgetProvider { public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // Perform this loop procedure for each widget that belongs to this // provider. for (int i=0; i < appWidgetIds.length; i++) { int appWidgetId = appWidgetIds[i]; // Create an Intent to launch ExampleActivity Intent intent = new Intent(context, ExampleActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity( /* context = */ context, /* requestCode = */ 0, /* intent = */ intent, /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); // Get the layout for the widget and attach an onClick listener to // the button. RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget_layout); views.setOnClickPendingIntent(R.id.button, pendingIntent); // Tell the AppWidgetManager to perform an update on the current app // widget. appWidgetManager.updateAppWidget(appWidgetId, views); } } }
此 AppWidgetProvider
只會定義 onUpdate()
方法,使用方法建立 PendingIntent
來啟動 Activity
,並使用 setOnClickPendingIntent(int,
PendingIntent)
將其附加至小工具的按鈕。其中包含一個迴圈,可疊代 appWidgetIds
中的每個項目。此陣列的 ID 陣列,可識別此提供者建立的每個小工具。假如使用者建立了多個小工具例項,則所有例項都會同時更新。不過,小工具的所有執行個體只會管理一個 updatePeriodMillis
排程。舉例來說,如果將更新排程定義為每兩小時,而在第一項更新一小時後,新增小工具的第二個執行個體,那麼系統在第一個定義的時間都會更新,並忽略第二個更新週期。兩者都會每兩小時更新一次,而不是每小時更新
詳情請參閱 ExampleAppWidgetProvider.java
範例類別。
接收小工具廣播意圖
AppWidgetProvider
是便利類別。如果想直接接收小工具廣播訊息,可以實作自己的 BroadcastReceiver
或覆寫 onReceive(Context,Intent)
回呼。您需要關注的意圖如下:
ACTION_APPWIDGET_UPDATE
ACTION_APPWIDGET_DELETED
ACTION_APPWIDGET_ENABLED
ACTION_APPWIDGET_DISABLED
ACTION_APPWIDGET_OPTIONS_CHANGED
建立小工具版面配置
您必須在 XML 中定義小工具的初始版面配置,並將其儲存在專案的 res/layout/
目錄中。詳情請參閱「設計指南」。
如果熟悉版面配置,建立小工具版面配置就相當簡單。不過請注意,小工具版面配置是以 RemoteViews
為基礎,而這種版面配置不支援所有種類的版面配置或檢視畫面小工具。您無法使用 RemoteViews
支援的檢視畫面自訂檢視畫面或子類別。
RemoteViews
也支援 ViewStub
,這是大小為零的隱藏 View
,可用於在執行階段延後加載版面配置資源。
支援有狀態行為
Android 12 使用下列現有元件,支援有狀態行為:
小工具仍為無狀態。您的應用程式必須儲存狀態並註冊狀態變更事件。
以下程式碼範例說明如何實作這些元件。
Kotlin
// Check the view. remoteView.setCompoundButtonChecked(R.id.my_checkbox, true) // Check a radio group. remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2) // Listen for check changes. The intent has an extra with the key // EXTRA_CHECKED that specifies the current checked state of the view. remoteView.setOnCheckedChangeResponse( R.id.my_checkbox, RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent) )
Java
// Check the view. remoteView.setCompoundButtonChecked(R.id.my_checkbox, true); // Check a radio group. remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2); // Listen for check changes. The intent has an extra with the key // EXTRA_CHECKED that specifies the current checked state of the view. remoteView.setOnCheckedChangeResponse( R.id.my_checkbox, RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent));
提供兩個版面配置:一個指定在 res/layout-v31
中搭載 Android 12 以上版本的裝置,另一個在預設 res/layout
資料夾中指定 Android 11 以下版本。
實作圓角
Android 12 推出了下列系統參數,可用於設定小工具圓角的半徑:
system_app_widget_background_radius
:小工具背景的圓角半徑,一律小於 28 dp。system_app_widget_inner_radius
:小工具內任何檢視畫面的圓角半徑。高於背景半徑 8 dp,以便在使用 8 dp 邊框間距時能夠完美對齊。
以下範例顯示小工具,使用 system_app_widget_background_radius
做為小工具角落,針對小工具內的檢視畫面使用 system_app_widget_inner_radius
。
1 小工具的邊角。
2 小工具中檢視畫面的邊角。
圓角的重要注意事項
- 第三方啟動器和裝置製造商可將
system_app_widget_background_radius
參數覆寫為小於 28 dp。system_app_widget_inner_radius
參數一律小於system_app_widget_background_radius
的值 8 dp。 - 如果小工具未使用
@android:id/background
,或是定義根據外框裁剪內容的背景 (將android:clipToOutline
設為true
),啟動器會自動識別背景,並使用最大 16 dp 的圓角矩形裁剪小工具。請參閱「確保小工具與 Android 12 相容」一節。
為了與舊版 Android 的小工具相容,建議您定義自訂屬性,並使用自訂主題來針對 Android 12 覆寫這些屬性,如以下 XML 檔案所示:
/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>
/layout/my_widget_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
android:background="@drawable/my_widget_background" />