建立簡易小工具

應用程式小工具是小型的應用程式檢視畫面,可嵌入其他 應用程式 (例如主畫面),並接收定期更新。這些 在使用者介面中稱為「小工具」,您可以發布 搭配應用程式小工具供應商 (或小工具供應商) 的一種。一種應用程式元件 保存其他小工具稱為應用程式小工具主機 (或小工具主機)。圖 1 會顯示一個音樂小工具範例:

音樂小工具範例
圖 1. 音樂小工具範例。

本文件說明如何使用小工具供應器發布小工具。適用對象 自行建立 AppWidgetHost 的詳細資訊 代管應用程式小工具,請參閱「建構小工具主機」。

如要瞭解如何設計小工具,請參閱「應用程式小工具總覽」。

小工具元件

如要建立小工具,您需要下列基本元件:

AppWidgetProviderInfo 物件
說明小工具的中繼資料,例如小工具的版面配置、更新 頻率和 AppWidgetProvider 類別 AppWidgetProviderInfo在 XML 中定義,如
AppWidgetProvider 類別
定義基本方法,讓您可以透過程式輔助方式 有了這個應用程式,你就能在更新小工具時接收廣播訊息 已啟用、已停用或刪除您AppWidgetProvider ,然後實作這項功能, 。
查看版面配置
定義小工具的初始版面配置。版面配置定義於 如本文件所述,使用 XML 格式

圖 2 顯示這些元件如何融入整體應用程式小工具處理作業 流程

應用程式小工具處理流程
圖 2. 應用程式小工具處理流程。
,瞭解如何調查及移除這項存取權。

如果需要使用者設定小工具,請實作應用程式小工具設定 活動。這個活動可讓使用者修改小工具設定,例如 時鐘小工具的時區

我們也建議採行以下改善項目:彈性小工具版面配置其他強化功能進階小工具集合小工具,以及建構小工具

宣告 AppWidgetProviderInfo XML

AppWidgetProviderInfo 物件會定義小工具的基本特質。 使用一項,在 XML 資源檔案中定義 AppWidgetProviderInfo 物件 <appwidget-provider> 元素,並儲存至專案的 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)、 minWidthminHeight
  • 自 Android 12 起, 「targetCellWidth」和「targetCellHeight」 屬性可指定小工具的預設大小 (以格線為單位) 儲存格。Android 11「會」忽略這些屬性 以下的數據,如果主畫面設定 支援格狀版面配置
  • minWidthminHeight 屬性會指定小工具的預設大小 單位。如果小工具的最小寬度或高度值不符 儲存格的尺寸,然後將值無條件進位到 最接近細胞的大小
,瞭解如何調查及移除這項存取權。 建議您同時指定 屬性:targetCellWidthtargetCellHeightminWidthminHeight,讓應用程式能改回使用 如果使用者的裝置為 minWidthminHeight 不支援 targetCellWidthtargetCellHeight。如果支援 targetCellWidthtargetCellHeight 屬性 的優先順序高於 minWidthminHeight 屬性。
minResizeWidth」和 minResizeHeight 指定小工具的絕對最小尺寸。這些值會指定 載入小工具後難以辨識或無法使用的尺寸。使用 這些屬性可讓使用者將小工具調整為更小的尺寸 大於預設的小工具大小minResizeWidth 屬性為 如果寬度大於 minWidth 或水平參數,就會遭到忽略 未啟用調整大小功能。詳情請見 resizeMode。同樣地, 如果 minResizeHeight 屬性大於,則系統會忽略該屬性 minHeight,或是未啟用垂直調整大小功能。
maxResizeWidth」和 maxResizeHeight 指定小工具的建議大小上限。如果這些值並非 格狀儲存格尺寸的倍數,會四捨五入至最接近的 儲存格大小。如果 maxResizeWidth 屬性有,系統會忽略該屬性 小於 minWidth,或是未水平調整其大小 請見 resizeMode。同樣地 如果 maxResizeHeight 屬性較大,則系統會忽略這個屬性 大於 minHeight,或是未啟用垂直調整大小功能。 相關元素已在 Android 12 中推出。
resizeMode 指定小工具可調整大小的規則。您可以使用 屬性讓主畫面小工具可水平、垂直方向調整 或兩軸都顯示使用者觸碰並按住小工具即可顯示其大小調整控點 然後拖曳水平或垂直控點來變更 版面配置格線resizeMode 屬性的值包括: horizontalverticalnone。目的地: 將小工具宣告為可水平和垂直調整大小,並使用 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 起:

使用 targetCellWidthtargetCellHeight 屬性做為預設值 這個小工具的大小

根據預設,小工具的大小為 2x2。您可以縮小至 2x1 或 最高可達 4x3。

Android 11 以下版本:

使用 minWidthminHeight 屬性計算

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

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

小工具的大小預設為 3x2。您可以縮小至 2x1 或 。

其他小工具屬性

下表說明相關的 <appwidget-provider> 屬性 不符合小工具尺寸以外的各種屬性

屬性與說明
updatePeriodMillis 定義小工具架構要求從 呼叫 onUpdate() 即可 AppWidgetProvider 回呼方法。實際的更新不保證一定會在 做為此值的資料集更新頻率,建議您儘可能隨 ,為了節省電池電力,每小時不超過一次。 如需選擇適當更新週期的完整注意事項清單, 看 小工具更新最佳化 內容
initialLayout 指向定義小工具版面配置的版面配置資源。
configure 定義使用者新增小工具時啟動的活動。 讓使用者設定小工具屬性詳情請見 讓使用者自行設定小工具。 從 Android 12 開始,應用程式可以略過初始 此外還會從 0 自動調整資源配置 您完全不必調整資源調度設定請參閱使用 小工具的預設設定
description 指定小工具挑選器的顯示說明, 相關元素已在 Android 12 中推出。
previewLayout (Android 12) 和 previewImage (Android 11 以下版本)
  • 自 Android 12 起, previewLayout 屬性會指定可擴充的預覽, 提供的 XML 版面配置設定為小工具的預設大小。理想情況下 指定為這個屬性的版面配置 XML,與 以便產生實際的小工具
  • 在 Android 11 以下版本中,previewImage 屬性可以指定小工具在顯示後的預覽畫面 也就是使用者選取應用程式小工具時會看到的畫面如果不是 使用者改為看到應用程式的啟動器圖示。這個 欄位對應 android:previewImage 中的 將 <receiver> 元素放在 AndroidManifest.xml 檔案。
,瞭解如何調查及移除這項存取權。 注意:建議您同時指定 previewImagepreviewLayout 屬性,讓應用程式能夠改回使用 在使用者的裝置不支援的情況下使用 previewImage previewLayout。詳情請參閱 與可擴充的回溯相容性 小工具預覽
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> 元素必須包含 <action> 元素, android:name 屬性。這項屬性會指定 AppWidgetProvider 接受 ACTION_APPWIDGET_UPDATE 廣播。這是唯一必須明確宣告的廣播訊息。 AppWidgetManager敬上 會自動將所有其他小工具廣播訊息傳送到 AppWidgetProvider,如 無從得知

<meta-data> 元素會指定 AppWidgetProviderInfo 資源和 須具備以下屬性:

  • android:name:指定中繼資料名稱。使用 android.appwidget.provider,即可將資料視為 AppWidgetProviderInfo 描述元。
  • android:resource:指定 AppWidgetProviderInfo 資源 或 HTTP/HTTPS 位置

實作 AppWidgetProvider 類別

AppWidgetProvider 類別會擴充 BroadcastReceiver,以 便利類別,可以處理小工具廣播訊息。只會收到事件 與小工具相關的廣播訊息,例如小工具更新時 已刪除、啟用和停用發生這類廣播事件時,會發生以下情況: 以下為呼叫 AppWidgetProvider 方法:

onUpdate()
系統會呼叫此方法,依照 AppWidgetProviderInfo 中的 updatePeriodMillis 屬性。請參閱表格 在本頁說明其他小工具屬性 瞭解詳情
,瞭解如何調查及移除這項存取權。
使用者新增小工具時,系統也會呼叫此方法,因此 例如為 View 物件或啟動要載入資料的工作 還可以顯示在小工具中不過,如果您宣告某個設定活動 configuration_optional 標記,則當使用者 新增小工具,但後續更新會呼叫此小工具。第一種是 執行第一次更新作業的責任 設定完成詳情請參閱「允許使用者設定應用程式小工具」。
,瞭解如何調查及移除這項存取權。
最重要的回呼是 onUpdate()。請參閱「使用 詳情請參閱本頁中的 onUpdate() 類別
onAppWidgetOptionsChanged()

初次放置小工具時,只要小工具出現 已調整大小。使用這個回呼,根據小工具的大小顯示或隱藏內容 範圍。取得大小範圍,並從 Android 12 開始 小工具執行個體所能接收的大小清單 (方法是呼叫 getAppWidgetOptions()、 會傳回一個 Bundle,其中包含 包括:

onDeleted(Context, int[])

每次從小工具主機刪除小工具時,系統就會呼叫此方法。

onEnabled(Context)

當初次建立小工具的例項時,系統會呼叫此方法。 舉例來說,如果使用者在小工具中加入兩個例項,系統只會呼叫 首次完成的任務如果需要開啟新的資料庫或執行 所有小工具執行個體只需要執行一次,就很適合 親自體驗

onDisabled(Context)

從 您可以在這裡清除 onEnabled(Context) 中的所有工作 例如刪除暫存資料庫

onReceive(Context, Intent)

針對每個廣播訊息、每個前一個回呼之前和之前,系統都會呼叫此方法 方法。您通常不需要實作此方法,因為 AppWidgetProvider 實作會篩選所有小工具廣播並呼叫 執行這些動作

您必須將 AppWidgetProvider 類別實作宣告為廣播訊息 使用 AndroidManifest 中的 <receiver> 元素接收端。請參閱宣告 小工具中的小工具,以取得更多資訊。

使用 onUpdate() 類別處理事件

最重要的 AppWidgetProvider 回呼是 onUpdate(),因為這是 將每個小工具新增到主機時呼叫 ,除非您使用設定 沒有 configuration_optional 旗標的活動。如果您的小工具接受任何 然後在這個回呼中註冊事件處理常式。如果 小工具不會建立暫存檔案或資料庫,也不會執行其他作業 但 onUpdate() 是唯一需要清除的回呼方法 這些 Pod 需要定義

舉例來說,如果您想讓小工具具有啟動活動時的按鈕 輕觸後,您可以使用下列的 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 排程 都會用於管理小工具的所有執行個體舉例來說,如果更新時間表 定義為每兩小時,接著新增一個小工具 並在第一個小時過後 1 小時,這兩種更新的時間都會按照 第一個更新期間,則會忽略第二個更新週期。他們每兩個端點都會更新一次 並非每小時

詳情請參閱 ExampleAppWidgetProvider.java敬上 範例類別。

接收小工具廣播意圖

AppWidgetProvider 是便利類別。如果希望收到小工具 直接播送,您可以使用自己的 BroadcastReceiver 或覆寫 這個 onReceive(Context,Intent) 回呼。您需要關注的意圖 包括:

建立小工具版面配置

您必須在 XML 中定義小工具的初始版面配置,並將其儲存至 專案的 res/layout/ 目錄。詳情請參閱設計說明 指南。

如果熟悉,建立小工具版面配置相當簡單 版面配置。不過請注意 以 RemoteViews、 不支援每一種版面配置或檢視小工具您無法使用自訂欄位 RemoteViews 支援的檢視區塊或子類別。

RemoteViews 也支援 ViewStub, 這是大小為零的隱藏 View,可用來延後加載版面配置 執行特定工作

支援有狀態行為

Android 12 新增了支援有狀態行為的下列項目 現有元件

小工具仍為無狀態。您的應用程式必須儲存狀態並註冊 狀態變更事件。

顯示有狀態行為的購物清單小工具範例
圖 3. 有狀態行為的示例。
,瞭解如何調查及移除這項存取權。

以下程式碼範例說明如何實作這些元件。

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

提供兩種版面配置:一種指定搭載 Android 12 或 res/layout-v31的升幅,而另一個 預設 res/layout 資料夾中包含 Android 11 以下版本。

實作圓角

Android 12 推出了下列系統參數,以便設定 小工具圓角的半徑:

下例中的小工具會使用 system_app_widget_background_radius 用於小工具角落,以及 針對小工具內的檢視畫面:system_app_widget_inner_radius

小工具顯示小工具背景的半徑,以及小工具內的檢視畫面
圖 4. 圓角。

1 小工具的邊角。

2 小工具中檢視畫面的邊角。

圓角的重要注意事項

  • 第三方啟動器和裝置製造商可以覆寫 system_app_widget_background_radius 參數必須小於 28 dp。 system_app_widget_inner_radius 參數一律小於 8 dp system_app_widget_background_radius 的值。
  • 如果小工具未使用 @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" />