应用微件是可以嵌入其他应用(如主屏幕)并接收定期更新的微型应用视图。这些视图在界面中称为微件,您可以使用应用微件提供程序(或微件提供程序)发布微件。容纳其他 widget 的应用组件称为应用 widget 宿主(或 widget 宿主)。图 1 显示了一个示例音乐 widget:

本文档介绍了如何使用 widget 提供程序发布 widget。如需详细了解如何创建自己的 AppWidgetHost
来托管应用 widget,请参阅构建 widget 托管应用。
如需了解如何设计微件,请参阅应用微件概览。
微件组件
如需创建 widget,您需要以下基本组件:
AppWidgetProviderInfo
对象- 描述 widget 的元数据,例如 widget 的布局、更新频率和
AppWidgetProvider
类。AppWidgetProviderInfo
是在 XML 中定义的,如本文档中所述。 AppWidgetProvider
类- 定义允许您以编程方式与 widget 连接的基本方法。通过它,您会在更新、启用、停用或删除 widget 时收到广播。您可以在清单中声明
AppWidgetProvider
,然后按照本文档中的说明实现该功能。 - 视图布局
- 定义 widget 的初始布局。布局在 XML 中定义,如本文档中所述。
图 2 显示了这些组件如何融入整个应用 widget 处理流程。

如果您的 widget 需要用户配置,请实现应用 widget 配置 activity。此 activity 可让用户修改 widget 设置,例如时钟 widget 的时区。
- 从 Android 12(API 级别 31)开始,您可以提供默认配置,并允许用户稍后重新配置 widget。如需了解详情,请参阅使用 widget 的默认配置和允许用户重新配置已放置的 widget。
- 在 Android 11(API 级别 30)或更低版本中,每当用户将 widget 添加到其主屏幕时,系统都会启动此 activity。
我们还建议您进行以下改进:灵活的 widget 布局、各种增强功能、高级 widget、集合 widget 和构建 widget 宿主。
声明 AppWidgetProviderInfo XML
AppWidgetProviderInfo
对象定义了 widget 的基本属性。您可以使用单个 <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>
微件尺寸设置属性
默认的主屏幕根据定义了高度和宽度的单元格的网格在其窗口中放置 widget。大多数主屏幕仅允许微件采用网格单元的整数倍大小,例如,水平方向上为两个单元格,垂直方向上为三个单元格。
借助微件尺寸调整属性,您可以为微件指定默认大小,并提供微件大小的下限和上限。在此背景下,widget 的默认大小是指 widget 首次添加到主屏幕时所占的大小。
下表介绍了与 widget 大小调整相关的 <appwidget-provider>
属性:
属性和说明 | |
---|---|
targetCellWidth 和 targetCellHeight (Android 12)、minWidth 和 minHeight |
targetCellWidth 和 targetCellHeight ,以及 minWidth 和 minHeight ),以便在用户设备不支持 targetCellWidth 和 targetCellHeight 时,您的应用可以回退到使用 minWidth 和 minHeight 。如果支持,targetCellWidth 和 targetCellHeight 属性优先于 minWidth 和 minHeight 属性。
|
minResizeWidth 和 minResizeHeight |
指定 widget 的绝对最小大小。这些值指定了微件小于什么尺寸会难以辨认或无法使用。使用这些属性,用户可以将微件的大小调整为小于默认微件大小。如果 minResizeWidth 属性的值大于 minWidth 或未启用水平大小调整,系统将忽略该属性。请参阅 resizeMode 。同样,如果 minResizeHeight 属性的值大于 minHeight 或未启用垂直大小调整,系统也会忽略该属性。 |
maxResizeWidth 和 maxResizeHeight |
指定 widget 的建议最大尺寸。如果这些值不是网格单元格尺寸的倍数,则会向上舍入为最接近的单元格大小。如果 maxResizeWidth 属性小于 minWidth 或未启用水平大小调整,系统将忽略该属性。请参阅 resizeMode 。同样,如果 maxResizeHeight 属性的值大于 minHeight 或未启用垂直大小调整,系统将忽略该属性。
在 Android 12 中引入。 |
resizeMode |
指定可以按什么规则来调整 widget 的大小。您可以使用此属性来让主屏幕 widget 在横轴上可调整大小、在纵轴上可调整大小,或者在这两个轴上均可调整大小。用户可轻触并按住微件以显示其大小调整手柄,然后拖动水平或垂直手柄以更改其在布局网格上的大小。resizeMode 属性的值包括 horizontal 、vertical 和 none 。如需将 widget 声明为在水平和垂直方向上均可调整大小,请使用 horizontal|vertical 。 |
示例
为说明上表中的属性如何影响 widget 大小调整,假设有以下规范:
- 网格单元格的宽度为 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
属性作为 widget 的默认大小。
默认情况下,该 widget 的大小为 2x2。该 widget 的大小可调整为最小 2x1 或最大 4x3。
Android 11 及更低版本:
使用 minWidth
和 minHeight
属性计算 widget 的默认大小。
默认宽度 = Math.ceil(80 / 30)
= 3
默认高度 = Math.ceil(80 / 50)
= 2
默认情况下,该 widget 的大小为 3x2。您可以将该 widget 调整为最小 2x1 或最大全屏。
其他 widget 属性
下表介绍了与 widget 大小调整以外的质量相关的 <appwidget-provider>
属性。
属性和说明 | |
---|---|
updatePeriodMillis |
定义 widget 框架通过调用 onUpdate() 回调方法从 AppWidgetProvider 请求更新的频率。不能保证实际更新按此值正好准时发生,我们建议尽可能降低更新频率(不超过每小时一次),以节省电池电量。
如需查看选择合适更新周期的完整注意事项列表,请参阅优化 widget 内容更新。 |
initialLayout |
指向用于定义 widget 布局的布局资源。 |
configure |
定义在用户添加 widget 时启动的 activity,以便用户配置 widget 属性。请参阅允许用户配置 widget。 从 Android 12 开始,应用可以跳过初始配置。如需了解详情,请参阅使用 widget 的默认配置。 |
description |
指定微件选择器要为您的微件显示的说明。在 Android 12 中引入。 |
previewLayout (Android 12)
和 previewImage (Android 11 及更低版本) |
previewImage 和 previewLayout 属性,以便在用户设备不支持 previewLayout 时,您的应用可以回退到使用 previewImage 。如需了解详情,请参阅可缩放微件预览的向后兼容性。
|
autoAdvanceViewId |
指定由 widget 的宿主自动跳转的 widget 子视图的视图 ID。 |
widgetCategory |
声明 widget 是否可以显示在主屏幕 (home_screen ) 和/或锁定屏幕 (keyguard ) 上。对于 Android 5.0 及更高版本,只有 home_screen 有效。 |
widgetFeatures |
声明 widget 支持的功能。例如,如果您希望微件在用户添加它时使用其默认配置,请同时指定 configuration_optional 和 reconfigurable 标志。这样会在用户添加微件后绕过启动配置 activity。用户以后仍可重新配置微件。 |
使用 AppWidgetProvider 类处理 widget 广播
AppWidgetProvider
类可处理 widget 广播,并根据 widget 生命周期事件更新 widget。以下部分介绍了如何在清单中声明 AppWidgetProvider
,然后实现它。
在清单中声明 widget
首先,在应用的 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
属性,该属性用于指定 widget 使用的 AppWidgetProvider
。除非单独的进程需要向您的 AppWidgetProvider
广播,否则不得导出该组件,但通常情况下不会出现这种情况。
<intent-filter>
元素必须包含一个具有 android:name
属性的 <action>
元素。此属性指定 AppWidgetProvider
接受 ACTION_APPWIDGET_UPDATE
广播。这是您必须明确声明的唯一一项广播。AppWidgetManager
会根据需要自动将其他所有 widget 广播发送到 AppWidgetProvider
。
<meta-data>
元素指定 AppWidgetProviderInfo
资源,并且需要以下属性:
android:name
:指定元数据名称。使用android.appwidget.provider
将数据标识为AppWidgetProviderInfo
描述符。android:resource
:指定AppWidgetProviderInfo
资源位置。
实现 AppWidgetProvider 类
AppWidgetProvider
类扩展了 BroadcastReceiver
,作为一个辅助类来处理 widget 广播。它仅接收与 widget 有关的事件广播,例如当更新、删除、启用和停用 widget 时发出的广播。当发生这些广播事件时,系统会调用以下 AppWidgetProvider
方法:
onUpdate()
- 调用此方法可以按
AppWidgetProviderInfo
中的updatePeriodMillis
属性定义的时间间隔来更新 widget。如需了解详情,请参阅本页面中描述其他 widget 属性的表格。 - 当用户添加 widget 时也会调用此方法,因此它会执行基本设置,例如为
View
对象定义事件处理脚本,或启动作业以加载要在 widget 中显示的数据。不过,如果您声明的配置 activity 没有configuration_optional
标志,则当用户添加 widget 时,系统不会调用此方法,但会调用它来执行后续更新。配置 activity 负责在配置完成后执行首次更新。如需了解详情,请参阅允许用户配置应用 widget。 - 最重要的回调是
onUpdate()
。如需了解详情,请参阅本页中的使用onUpdate()
类处理事件。 onAppWidgetOptionsChanged()
当首次放置 widget 时以及每当调整 widget 的大小时,会调用此方法。您可以使用此回调来根据微件的大小范围显示或隐藏内容。通过调用
getAppWidgetOptions()
获取大小范围,以及(从 Android 12 开始)widget 实例可采用的可能大小列表,该方法会返回包含以下各项的Bundle
:OPTION_APPWIDGET_MIN_WIDTH
:包含微件实例的宽度的下限(以 dp 为单位)。OPTION_APPWIDGET_MIN_HEIGHT
:包含微件实例的高度下限(以 dp 为单位)。OPTION_APPWIDGET_MAX_WIDTH
:包含微件实例的宽度上限(以 dp 为单位)。OPTION_APPWIDGET_MAX_HEIGHT
:包含微件实例的高度上限(以 dp 为单位)。OPTION_APPWIDGET_SIZES
:包含 widget 实例可采用的可能大小 (List<SizeF>
) 的列表,以 dp 为单位。在 Android 12 中引入。
onDeleted(Context, int[])
每次从微件托管应用中删除微件时,都会调用此方法。
onEnabled(Context)
首次创建微件的实例时,会调用此方法。例如,如果用户添加了两个 widget 实例,则只有在首次添加时才会调用此方法。如果您需要打开一个新的数据库或执行只需要对所有 widget 实例执行一次的其他设置,则此方法非常合适。
onDisabled(Context)
当从微件宿主中删除微件的最后一个实例时,会调用此方法。您应使用此方法来清理在
onEnabled(Context)
中完成的所有工作,如删除临时数据库。onReceive(Context, Intent)
针对每个广播调用此方法,并且是在上述各个回调方法之前调用。您通常不需要实现此方法,因为默认的
AppWidgetProvider
实现会过滤所有 widget 广播并视情况调用上述方法。
您必须在 AndroidManifest
中使用 <receiver>
元素将 AppWidgetProvider
类实现声明为广播接收器。如需了解详情,请参阅本页面中的在清单中声明 widget。
使用 onUpdate() 类处理事件
最重要的 AppWidgetProvider
回调是 onUpdate()
,因为向宿主应用添加每个 widget 时都会调用它,除非您使用不含 configuration_optional
标志的配置 activity。如果您的 widget 接受任何用户互动事件,请在此回调中注册事件处理脚本。如果您的 widget 未创建临时文件或数据库,或者未执行其他需要清理的工作,则 onUpdate()
可能是您需要定义的唯一一个回调方法。
例如,如果您希望微件具有一个在用户点按时会启动 activity 的按钮,则可以使用以下 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()
方法,并使用该方法创建用于启动 Activity
的 PendingIntent
,然后使用 setOnClickPendingIntent(int,
PendingIntent)
将其附加到 widget 的按钮。它包含一个遍历 appWidgetIds
(这是一个 ID 数组,标识由此提供程序创建的每个 widget)中每个条目的循环。如果用户创建了微件的多个实例,则它们会全部同时更新。不过,对于微件的所有实例,只管理一个 updatePeriodMillis
时间表。例如,如果将更新时间表定义为每两小时更新一次,并且在第一个 widget 实例添加一小时后添加了第二个 widget 实例,那么这两个 widget 实例都会按照第一个 widget 实例定义的周期进行更新,而第二个更新周期会被忽略。这两个数据源每两小时更新一次,而不是每小时更新一次。
如需了解详情,请参阅 ExampleAppWidgetProvider.java
示例类。
接收 widget 广播 intent
AppWidgetProvider
是一个便利类。如果您希望直接接收 widget 广播,您可以实现自己的 BroadcastReceiver
或替换 onReceive(Context,Intent)
回调。您需要关注的 intent 如下所示:
ACTION_APPWIDGET_UPDATE
ACTION_APPWIDGET_DELETED
ACTION_APPWIDGET_ENABLED
ACTION_APPWIDGET_DISABLED
ACTION_APPWIDGET_OPTIONS_CHANGED
创建 widget 布局
您必须在 XML 中定义 widget 的初始布局,并将其保存在项目的 res/layout/
目录中。如需了解详情,请参阅设计指南。
如果您熟悉布局,那么创建 widget 布局非常简单。不过,请注意,微件布局基于 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));
提供两个布局:一个的目标设备搭载 Android 12 或更高版本(位于 res/layout-v31
中),另一个的目标设备搭载以前的 Android 11 或更低版本(位于默认的 res/layout
文件夹中)。
实现圆角
Android 12 引入了以下系统参数来设置微件圆角的半径:
system_app_widget_background_radius
:微件背景的圆角半径,绝不会大于 28 dp。内半径,可根据外半径和边衬区计算得出。 请参阅以下代码段:
/** * Applies corner radius for views that are visually positioned [widgetPadding]dp inside of the * widget background. */ @Composable fun GlanceModifier.appWidgetInnerCornerRadius(widgetPadding: Dp): GlanceModifier { if (Build.VERSION.SDK_INT < 31) { return this } val resources = LocalContext.current.resources // get dimension in float (without rounding). val px = resources.getDimension(android.R.dimen.system_app_widget_background_radius) val widgetBackgroundRadiusDpValue = px / resources.displayMetrics.density if (widgetBackgroundRadiusDpValue < widgetPadding.value) { return this } return this.cornerRadius(Dp(widgetBackgroundRadiusDpValue - widgetPadding.value)) }
如需计算 widget 内部内容的合适半径,请使用以下公式:systemRadiusValue - widgetPadding
将内容剪裁为非矩形形状的 widget 应使用 @android:id/background
作为背景视图的视图 ID,并将 android:clipToOutline
设置为 true
。
有关圆角的重要注意事项
- 第三方启动器和设备制造商可以替换
system_app_widget_background_radius
参数,使其小于 28 dp。 如果您的 widget 不使用
@android:id/background
或定义基于轮廓剪裁其内容的背景(将android:clipToOutline
设置为true
),启动器会自动识别背景,并使用设置为系统半径的圆角矩形剪裁 widget。非矩形形状需要包含在圆角矩形调整大小容器中,以免被剪裁。
从 Android 16 开始,
system_app_widget_background_radius
的 AOSP 系统值为24dp
。启动器和设备制造商可能会将微件剪裁到system_app_widget_background_radius
。widget 的内部内容必须具有足够的内边距,以支持高达
28dp
的system_app_widget_background_radius
半径值,从而避免圆角裁剪内容。
为了确保微件与以前的 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" />