应用微件是小型应用视图,可嵌入到 应用(例如主屏幕)并定期接收更新。这些 视图在界面中称为“微件”,您可以发布 一个具有应用 widget 提供程序(或 widget 提供程序)。一个应用组件 称为应用 widget 宿主(或 widget 宿主)。图 1 显示示例音乐微件:
本文档介绍了如何使用 widget 提供程序发布 widget。对于
详细了解如何自行创建 AppWidgetHost
托管应用 widget,请参阅构建 widget 宿主。
如需了解如何设计 widget,请参阅应用 widget 概览。
微件组件
如需创建 widget,您需要以下基本组件:
AppWidgetProviderInfo
对象- 描述 widget 的元数据,例如 widget 的布局、更新
频率和
AppWidgetProvider
类别。AppWidgetProviderInfo
在 XML 中定义,如下所示 如本文档中所述。 AppWidgetProvider
类- 定义可让您以程序化方式与
微件。通过它,您可以在 widget 更新时收到广播,
已启用、已停用或删除您在
AppWidgetProvider
然后实现清单, 如本文档中所述。 - 视图布局
- 定义 widget 的初始布局。该布局在 XML(如本文档中所述)。
图 2 显示了这些组件如何融入整个应用 widget 处理过程 。
。如果您的 widget 需要用户配置,请实现应用 widget 配置 活动。此 activity 允许用户修改 widget 设置,例如 时区。
- 从 Android 12(API 级别 31)开始,您可以提供默认值 配置,让用户稍后重新配置该 widget。请参阅使用 微件的默认配置和启用 用户重新配置已放置的微件 了解详情。
- 在 Android 11(API 级别 30)或更低版本中,每次都会启动此 activity 用户将微件添加到其主屏幕。
此外,我们还建议您实现以下改进:灵活的微件布局、其他增强功能、高级微件、集合微件以及构建微件 主机。
声明 AppWidgetProviderInfo XML
AppWidgetProviderInfo
对象定义了 widget 的基本特性。
在 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>
微件大小调整属性
默认主屏幕会根据单元格网格将 widget 放置在其窗口中 具有已指定高度和宽度的广告素材大多数主屏幕仅允许微件显示 大小是网格单元格的整数倍(例如,两个单元格) 即水平方向上显示的是三个单元格。
使用微件大小调整属性,您可以为微件指定默认大小, 用于提供 widget 尺寸的下限和上限。在这种情况下, 微件的默认大小是首次启动微件时,微件采用的尺寸。 已添加到主屏幕。
下表介绍了与 <appwidget-provider>
调整微件大小:
属性和说明 | |
---|---|
targetCellWidth 和
targetCellHeight (Android 12)、
minWidth 和minHeight |
targetCellWidth 和
targetCellHeight 、minWidth 和
minHeight - 以便您的应用可以回退到使用
minWidth 和 minHeight (如果用户的设备)
不支持 targetCellWidth 和
targetCellHeight 。如果支持,
targetCellWidth 和 targetCellHeight 属性
优先于 minWidth 和 minHeight
属性。
|
minResizeWidth 和
minResizeHeight |
指定 widget 的绝对最小尺寸。这些值指定
使微件难以辨认或无法使用的尺寸。使用
通过这些属性,用户可以将微件的尺寸调整为
默认微件大小。minResizeWidth 属性为
如果值大于 minWidth 或水平值,则会被忽略
调整大小。请参阅
resizeMode 。同样,
如果 minResizeHeight 属性的值大于
minHeight ,或者未启用垂直大小调整。 |
maxResizeWidth 和
maxResizeHeight |
指定微件的建议大小上限。如果值不是
网格单元格尺寸的倍数,则会四舍五入到最接近
单元格大小。如果 maxResizeWidth 属性满足以下条件,则会被忽略:
小于 minWidth ,或者无法水平调整大小
。请参阅 resizeMode 。同样,
如果 maxResizeHeight 属性大于该值,则系统会忽略该属性
高于 minHeight 或者未启用垂直大小调整。
此元素在 Android 12 中引入。 |
resizeMode |
指定可按什么规则来调整微件的大小。您可以使用
属性使主屏幕 widget 可在水平方向、垂直方向上调整大小,
或同时在两个轴上绘制用户触摸和按住微件可显示其大小调整手柄,
然后拖动水平或垂直手柄,在
布局网格。resizeMode 属性的值包括
horizontal 、vertical 和none 。接收者
将 widget 声明为在水平和垂直方向上均可调整大小,请使用
horizontal|vertical 。 |
示例
为了说明上表中的属性对微件大小调整有何影响, 采用以下规范:
- 网格单元格的宽度为 30dp,高度为 50dp。
- 提供了以下属性规范:
<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 的尺寸。
默认情况下,微件的大小为 2x2。该 widget 可以缩小到 2x1 或 最大为 4x3。
Android 11 及更低版本:
使用 minWidth
和 minHeight
属性计算
。
默认宽度 = Math.ceil(80 / 30)
= 3
默认高度 = Math.ceil(80 / 50)
= 2
此微件的大小默认为 3x2。该 widget 可以缩小到 2x1 或 最高为全屏模式。
其他微件属性
下表介绍了与 <appwidget-provider>
例如微件大小调整
属性和说明 | |
---|---|
updatePeriodMillis |
定义微件框架从
AppWidgetProvider (通过调用 onUpdate() )
回调方法。我们无法保证实际更新会在
该值,我们建议您尽可能降低更新频率
(最多每小时一次)来节省电量。
如需查看选择适当更新周期时的注意事项的完整列表,
请参阅
针对更新微件的优化
内容。 |
initialLayout |
指向用于定义 widget 布局的布局资源。 |
configure |
定义用户添加 widget 时启动的 activity。 让他们可以配置 widget 属性。请参阅 允许用户配置微件。 从 Android 12 开始,您的应用可以跳过初始 配置。请参阅使用 微件的默认配置了解详情。 |
description |
指定要为 微件。此元素在 Android 12 中引入。 |
previewLayout (Android 12)
和 previewImage (Android 11 及更低版本) |
previewImage
和 previewLayout 属性,以便您的应用可以回退
如果用户的设备不支持,可以使用 previewImage
previewLayout 。有关详情,请参阅
具有可扩缩性的向后兼容性
微件预览。
|
autoAdvanceViewId |
指定自动推进的微件子视图的视图 ID 该 widget 的宿主。 |
widgetCategory |
声明 widget 是否可以显示在主屏幕上
(home_screen )、锁定屏幕 (keyguard ) 或
两者都有。对于 Android 5.0 及更高版本,只有 home_screen 有效。
|
widgetFeatures |
声明 widget 支持的功能。例如,如果您希望
您的 widget 在用户添加该 widget 时使用其默认配置,指定
和
configuration_optional
和
reconfigurable
标志。这会绕过在用户使用应用后
添加该微件。用户仍然可以
重新配置 widget
。 |
使用 AppWidgetProvider 类处理微件广播
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>
元素必须包含一个带有<action>
android:name
属性。此属性会指定 AppWidgetProvider
接受
ACTION_APPWIDGET_UPDATE
广播。这是您必须明确声明的唯一一项广播。通过
AppWidgetManager
自动将所有其他 widget 广播发送到 AppWidgetProvider
,如下所示:
。
<meta-data>
元素指定了 AppWidgetProviderInfo
资源,
需要以下属性:
android:name
:指定元数据名称。使用android.appwidget.provider
:将数据标识为AppWidgetProviderInfo
描述符。android:resource
:指定AppWidgetProviderInfo
资源 位置。
实现 AppWidgetProvider 类
AppWidgetProvider
类扩展了
BroadcastReceiver
作为
便捷类来处理微件广播。它仅接收事件
与 widget 相关的广播,例如当 widget 更新时,
删除、启用和停用当这些广播事件发生时,以下事件:
调用 AppWidgetProvider
方法:
onUpdate()
- 调用此方法可以按
AppWidgetProviderInfo
中的updatePeriodMillis
属性。请参阅表格 其他微件属性 。 。
- 当用户添加 widget 时,也会调用此方法,以便执行
基本设置,例如为容器定义事件处理脚本,
要加载数据的
View
对象或启动作业 。不过,如果您声明了一个配置 activity,而没有configuration_optional
标记之后,如果用户指定其值,系统不会调用此方法 会添加该 widget,但后续更新会调用它。它是 由配置 activity 负责执行首次更新, 配置完成。如需了解详情,请参阅允许用户配置应用微件。 。
- 最重要的回调是
onUpdate()
。请参阅使用onUpdate()
类。 onAppWidgetOptionsChanged()
首次放置 widget 时调用,每当 widget 被放置时,都会调用此方法。 大小。使用此回调可根据微件的大小显示或隐藏内容 范围。获取尺寸范围,并且从 Android 12 开始, 微件实例可以采用的可能大小的列表(通过调用
getAppWidgetOptions()
、 它会返回一个Bundle
,其中包含 以下:OPTION_APPWIDGET_MIN_WIDTH
: 包含 widget 实例宽度的下限(以 dp 为单位)。OPTION_APPWIDGET_MIN_HEIGHT
: 包含 widget 实例高度的下限(以 dp 为单位)。OPTION_APPWIDGET_MAX_WIDTH
: 包含 widget 实例宽度的上限(以 dp 为单位)。OPTION_APPWIDGET_MAX_HEIGHT
: 包含 widget 实例高度的上限(以 dp 为单位)。OPTION_APPWIDGET_SIZES
: 包含可能尺寸的列表 (List<SizeF>
),以 dp 为单位, widget 实例所能执行的操作。此元素在 Android 12 中引入。
onDeleted(Context, int[])
每次从 widget 宿主中删除 widget 时,都会调用此方法。
onEnabled(Context)
首次创建 widget 的实例时调用此方法。 例如,如果用户添加了微件的两个实例,则仅调用 第一次使用如果您需要打开一个新数据库或执行 所有微件实例只需要执行一次, 。
onDisabled(Context)
当 widget 的最后一个实例从 widget 托管应用。您可以在此处清理
onEnabled(Context)
中完成的所有工作。 例如删除临时数据库onReceive(Context, Intent)
系统会为每个广播调用此方法,且此操作是在前面的每个回调之前调用的 方法。您通常不需要实现此方法,因为默认情况下
AppWidgetProvider
实现会过滤所有 widget 广播并调用 方法。
您必须将 AppWidgetProvider
类实现声明为广播
在 AndroidManifest
中使用 <receiver>
元素的接收器。请参阅声明
“微件”。
使用 onUpdate() 类处理事件
最重要的 AppWidgetProvider
回调是 onUpdate()
,因为它
会在每个 widget 添加到托管组件时调用,除非您使用配置
不含 configuration_optional
标志的 activity。如果微件接受
用户互动事件,然后在此回调中注册事件处理脚本。如果
您的微件不会创建临时文件或数据库,也不会执行其他工作
onUpdate()
可能是您需要清理的唯一回调方法
所需的定义
例如,如果您需要一个带有按钮的 widget,该按钮可在
则可以使用 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
并将其附加到 widget 的
按钮使用 setOnClickPendingIntent(int,
PendingIntent)
。它包含一个遍历每个条目的循环
appWidgetIds
,这是 ID 数组,用于标识由
该提供商。如果用户创建了 widget 的多个实例,则
它们会同时更新但是,只有一个 updatePeriodMillis
时间表
对 widget 的所有实例进行管理。例如,如果更新时间表
定义为每两小时,并且会添加 widget 的第二个实例,
那么它们都会按照
并忽略第二个更新周期。它们每 2 次更新一次
而不是每小时
请参阅
ExampleAppWidgetProvider.java
示例类。
接收 widget 广播 intent
AppWidgetProvider
是一个辅助类。如果您想接收小程序
广播,因此您可以实现自己的 BroadcastReceiver
或替换
该
onReceive(Context,Intent)
回调。您需要关注的意图是
以下:
ACTION_APPWIDGET_UPDATE
ACTION_APPWIDGET_DELETED
ACTION_APPWIDGET_ENABLED
ACTION_APPWIDGET_DISABLED
ACTION_APPWIDGET_OPTIONS_CHANGED
创建 widget 布局
您必须在 XML 中定义微件的初始布局,并将其保存在
项目的 res/layout/
目录下。请参阅设计
指南了解详情。
如果您熟悉
layouts。但请注意
布局基于 RemoteViews
,
但不一定支持所有类型的布局或视图 widget。您不能使用自定义
视图或 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。system_app_widget_inner_radius
: 微件内任何视图的角半径。正好是 8dp 小于背景半径,以便在使用 8dp 时很好地对齐 内边距。
以下示例展示了一个微件,为微件的角使用 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
的值。 - 如果 widget 不使用
@android:id/background
或定义背景 使用android:clipToOutline
根据大纲裁剪其内容, 设为true
- 启动器会自动识别背景和 使用圆角最大为 16 dp 的矩形裁剪 widget。 请参阅确保您的微件与 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" />