改进 widget

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

使用动态配色

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

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

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

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

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

在以下使用 Material 3 主题的示例中,设备的主题颜色为“紫色”。强调色和微件背景会根据浅色模式和深色模式进行调整,如图 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. 采用浅色主题的微件。
采用深色模式主题的微件
图 2. 采用深色主题的微件。

动态配色的向后兼容性

动态配色仅适用于搭载 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 的用户语音指令。当用户通过发出“Hey Google,我本周在 ExampleApp 上跑了多少英里?”这样的请求来触发此 BII 时,Google 助理会主动显示您的 widget。

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

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

借助 Android 12,您可以添加放大的微件预览和微件说明。借助 Android 15,您可以使用生成的微件预览来改进应用的微件选择器体验。

如需改进应用的微件选择器体验,请在 Android 15 及更高版本的设备上提供生成的微件预览,在 Android 12 到 Android 14 设备上提供放大的微件预览(通过指定 previewLayout),并在更低版本的设备上提供 previewImage

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

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

应用可以使用 AppWidgetManagersetWidgetPreviewgetWidgetPreview 方法,使用最新的个性化信息更新其微件的外观。

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

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

以下示例使用 Jetpack Glance 生成更新后的预览。必须将 compileSdk 构建设置设为 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 微件布局资源,并将其设置为预览。必须将 compileSdk build 设置设为 35 或更高版本,才能让 setWidgetPreview 在此代码段中显示为方法。

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

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

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

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

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

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

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

构建准确预览的推荐方法

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

<appwidget-provider
    ...
    android:previewLayout="@layout/my_widget_preview">
</appwidget-provider>
显示微件预览的图片
图 3. 默认显示在 3x3 区域内的微件预览,但由于其 XML 布局,可以放入 3x1 区域。

如需显示准确的预览,您可以通过完成以下步骤,直接为实际 widget 布局提供默认值:

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

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

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

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

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

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

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

为微件添加名称

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

系统会从 AndroidManifest.xml 文件中 widget 的 receiver 元素的 label 属性加载 widget 的名称。

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

为微件添加说明

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

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

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

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

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

实现更流畅的过渡

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

为了实现这种改进的过渡,请使用 @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);