改进 widget

尝试使用 Compose 方式
Jetpack Compose 是推荐用于 Android 的界面工具包。了解如何使用 Compose 风格的 API 构建 widget。

本页详细介绍了从 Android 12(API 级别 31)开始提供的可选 widget 增强功能。这些功能是可选的,但实现起来非常简单,并且可以改善用户的 widget 体验。

使用动态配色

从 Android 12 开始,微件可以为按钮、背景及其他组件使用设备主题颜色。这样可使过渡更流畅,而且还能在不同的 widget 之间保持一致。

您可以通过以下两种方式实现动态颜色:

在根布局中设置主题后,您可以在根布局或其任何子布局中使用通用颜色属性来获取动态颜色。

您可以使用的颜色属性的一些示例如下:

  • ?attr/primary
  • ?attr/primaryContainer
  • ?attr/onPrimary
  • ?attr/onPrimaryContainer

在以下使用 Material 3 主题的示例中,设备的颜色主题为“偏紫色”。强调色和 widget 背景会根据浅色模式和深色模式进行调整,如图 1 和图 2 所示。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="?attr/colorPrimaryContainer"
  android:theme="@style/Theme.Material3.DynamicColors.DayNight">

  <ImageView
    ...
    app:tint="?attr/colorPrimaryContainer"
    android:src="@drawable/ic_partly_cloudy" />

    <!-- Other widget content. -->

</LinearLayout>
采用浅色模式主题的微件
图 1. 采用浅色主题的 widget。
采用深色模式主题的微件
图 2. 深色主题中的 widget。

动态颜色的向后兼容性

动态颜色仅适用于搭载 Android 12 或更高版本的设备。如需为较低版本提供自定义主题,请使用默认主题属性创建包含自定义颜色的默认主题和新的限定符 (values-v31)。

下面是一个使用 Material 3 主题的示例:

/values/styles.xml

<resources>
  <style name="MyWidgetTheme" parent="Theme.Material3.DynamicColors.DayNight">
    <!-- Override default colorBackground attribute with custom color. -->
    <item name="android:colorBackground">@color/my_background_color</item>

    <!-- Add other colors/attributes. -->

  </style>
</resources>

/values-v31/styles.xml

<resources>
  <!-- Do not override any color attribute. -->
  <style name="MyWidgetTheme" parent="Theme.Material3.DynamicColors.DayNight" />
</resources>

/layout/my_widget_layout.xml

<resources>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:background="?android:attr/colorBackground"
    android:theme="@style/MyWidgetTheme" />
</resources>

启用语音支持

借助与应用有关的 Action,Google 助理可以显示 widget 来响应相关的用户语音指令。通过将 widget 配置为响应内置 intent (BII),您的应用可以在 Google 助理平台(例如 Android 和 Android Auto)上主动显示 widget。用户可以选择将 Google 助理显示的 widget 固定到其启动器,从而鼓励用户日后继续互动。

例如,您可以为锻炼应用的锻炼总结 widget 配置 GET_EXERCISE_OBSERVATION BII,以执行触发该 BII 的用户语音指令。当用户通过发出“Hey Google,我本周在 ExampleApp 上跑了多少英里?”之类的请求来触发此 BII 时,Google 助理会主动显示您的 widget。

有数十个 BII 涵盖了多种用户互动类别,几乎所有 Android 应用都可以通过这些 BII 增强其 widget 的语音功能。如需开始集成,请参阅将与应用有关的 Action 与 Android widget 集成

改进应用的微件选择器体验

在 Android 12 中,您可以添加缩放的 widget 预览和 widget 说明。Android 15 可让您通过生成的 widget 预览来改进应用的 widget 选择器体验。

为了改进应用的 widget 选择器体验,请在搭载 Android 15 及更高版本的设备上提供生成的 widget 预览,在搭载 Android 12 至 Android 14 的设备上提供缩放的 widget 预览(通过指定 previewLayout),并在更早的版本中提供 previewImage

向微件选择器添加生成的微件预览

应用必须在模块 build.gradle 文件中将 compileSdk 值设置为 35 或更高值,才能在 Android 15 或更高版本的设备上向 widget 选择器提供 RemoteViews。这意味着应用可以更新选择器中的内容,使其更能代表用户看到的内容。

应用可以使用 AppWidgetManagersetWidgetPreviewgetWidgetPreview 方法,通过最新且个性化的信息更新 widget 的外观。

使用 Jetpack Glance 生成更新后的预览

Glance.compose 运行一个组合,因此可组合项的正文中不使用任何挂起函数、flow 或类似的异步调用。您必须改用常量数据。

以下示例使用 Jetpack Glance 生成更新后的预览。 compileSdk 的 build 设置必须为 35 或更高版本,setWidgetPreview 才能在此代码段中显示为方法。

AppWidgetManager.getInstance(appContext).setWidgetPreview(
    ComponentName(
        appContext,
        ExampleAppWidgetReceiver::class.java
    ),
    AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
    ExampleAppWidget().compose(
        context = appContext
    ),
)

生成不含 Jetpack Glance 的更新预览

您可以在不使用 Glance 的情况下使用 RemoteViews。以下示例加载 XML widget 布局资源并将其设置为预览。如果 setWidgetPreview 要在此代码段中显示为方法,则必须将 compileSdk build 设置设为 35 或更高版本。

AppWidgetManager.getInstance(appContext).setWidgetPreview(
    ComponentName(
        appContext,
        ExampleAppWidgetReceiver::class.java
    ),
    AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
    RemoteViews("com.example", R.layout.widget_preview)
)

向微件选择器添加可缩放的微件预览

从 Android 12 开始,微件选择器中显示的微件预览可缩放。您会将其作为 XML 布局提供,该布局设置为微件的默认大小。以前,微件预览是静态可绘制资源,在某些情况下,会导致预览无法准确反映微件添加到主屏幕后的显示效果。

如需实现可缩放的 widget 预览,请改用 appwidget-provider 元素的 previewLayout 属性来提供 XML 布局:

<appwidget-provider
    android:previewLayout="@layout/my_widget_preview">
</appwidget-provider>

我们建议使用与实际 widget 相同的布局,并使用真实的默认值或测试值。大多数应用都使用相同的 previewLayoutinitialLayout。如需有关创建准确的预览版布局的指导,请参阅本页中的以下部分。

我们建议您同时指定 previewLayoutpreviewImage 属性,以便在用户设备不支持 previewLayout 时,您的应用可以回退到使用 previewImagepreviewLayout 属性的优先级高于 previewImage 属性。

构建准确预览的推荐方法

如需实现可缩放的 widget 预览,请使用 appwidget-provider 元素的 previewLayout 属性来提供 XML 布局:

<appwidget-provider
    ...
    android:previewLayout="@layout/my_widget_preview">
</appwidget-provider>
显示 widget 预览的图片
图 3. 默认情况下显示在 3x3 区域中,但由于其 XML 布局,可以适应 3x1 区域的 widget 预览。

如需显示准确的预览效果,您可以直接提供具有默认值的实际 widget 布局,只需完成以下步骤:

  • TextView 元素设置 android:text="@string/my_widget_item_fake_1"

  • ImageView 组件设置默认图片、占位图片或图标,例如 android:src="@drawable/my_widget_icon"

如果没有默认值,预览可能会显示不正确的值或空值。此方法的一个重要优势是,您可以提供本地化的预览内容。

如需了解包含 ListViewGridViewStackView 的更复杂预览的推荐方法,请参阅构建包含动态项的准确预览了解详情。

可缩放微件预览的向后兼容性

如需让 Android 11(API 级别 30)或更低版本中的 widget 选择器显示 widget 的预览,请指定 previewImage 属性。

如果您更改了微件的外观,请更新预览图片。

为微件添加名称

widget 在 widget 选择器中显示时需要具有唯一的名称。

微件的名称是从 AndroidManifest.xml 文件中微件的 receiver 元素的 label 属性加载的。

<receiver
    ….
   android:label="Memories">
     ….
</receiver>

为微件添加说明

从 Android 12 开始,为微件提供要由微件选择器显示的说明。

一张图片,显示了 widget 选择器,其中显示了一个 widget 及其说明
图 4. 显示 widget 及其说明的 widget 选择器示例。

使用 &lt;appwidget-provider&gt; 元素的 description 属性为 widget 提供说明:

<appwidget-provider
    android:description="@string/my_widget_description">
</appwidget-provider>

您可以在以前的 Android 版本中使用 descriptionRes 属性,但微件选择器会忽略该属性。

实现更流畅的过渡

从 Android 12 开始,当用户从 widget 启动您的应用时,启动器会提供更流畅的过渡。

为了实现这种改进的过渡,请使用 @android:id/backgroundandroid.R.id.background 标识背景元素:

// Top-level layout of the widget.
<LinearLayout
    android:id="@android:id/background">
</LinearLayout>

您的应用可以在以前的 Android 版本中使用 @android:id/background,而不会出现中断,但该属性会被忽略。

使用 RemoteViews 的运行时修改

从 Android 12 开始,您可以利用几个 RemoteViews 方法在运行时修改 RemoteViews 属性。如需查看所添加方法的完整列表,请参阅 RemoteViews API 参考文档。

以下代码示例展示了如何使用其中的一些方法。

Kotlin

// Set the colors of a progress bar at runtime.
remoteView.setColorStateList(R.id.progress, "setProgressTintList", createProgressColorStateList())

// Specify exact sizes for margins.
remoteView.setViewLayoutMargin(R.id.text, RemoteViews.MARGIN_END, 8f, TypedValue.COMPLEX_UNIT_DP)

Java

// Set the colors of a progress bar at runtime.
remoteView.setColorStateList(R.id.progress, "setProgressTintList", createProgressColorStateList());

// Specify exact sizes for margins.
remoteView.setViewLayoutMargin(R.id.text, RemoteViews.MARGIN_END, 8f, TypedValue.COMPLEX_UNIT_DP);