增强您的窗口小部件

此页面包含从 Android 12(API 级别 31)开始提供的可选小部件增强的详细信息。这些功能是可选的,但它们易于实现,并且可以改善用户的窗口小部件体验。

使用动态颜色

从 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>
Widget in light mode theme
图 1. 亮主题中的小部件。
Widgets in dark mode theme
图 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>

启用语音支持

App Actions 允许 Google 助理响应相关的用户语音命令显示小部件。通过将您的窗口小部件配置为响应内置意图 (BII),您的应用可以在 Android 和 Android Auto 等助理界面上主动显示小部件。用户可以选择将助理显示的小部件固定到他们的启动器,从而鼓励未来的互动。

例如,您可以将锻炼应用的锻炼总结小部件配置为满足触发GET_EXERCISE_OBSERVATION BII 的用户语音命令。当用户通过发出类似“嘿 Google,本周我在 ExampleApp 上跑了多少英里?”之类的请求触发此 BII 时,助理会主动显示您的窗口小部件。

有数十个 BII 涵盖多个用户交互类别,几乎所有 Android 应用都可以增强其小部件以支持语音。要开始使用,请参阅将 App Actions 与 Android 小部件集成

改进您的应用的小部件选择器体验

Android 12 允许您添加缩放的小部件预览和小部件说明。Android 15 允许您使用生成的窗口小部件预览来改善应用的小部件选择器体验。

要改善您应用的小部件选择器体验,请在 Android 15 及更高版本的设备上提供生成的窗口小部件预览,为 Android 12 到 Android 14 设备提供缩放的小部件预览(通过指定previewLayout),并为早期版本提供previewImage

向小部件选择器添加生成的窗口小部件预览

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

应用可以使用AppWidgetManagersetWidgetPreviewgetWidgetPreview方法使用最新和个性化的信息更新其窗口小部件的外观。

使用 Jetpack Glance 生成更新的预览

Glance.compose运行一次组合,因此在可组合体的正文中不使用 suspend 函数、流或类似的异步调用。相反,您必须使用常量数据。

以下示例使用 Jetpack Glance 生成更新的预览。setWidgetPreview需要 35 或更高的compileSdk构建设置才能在此代码段中显示为方法。

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

无需 Jetpack Glance 即可生成更新的预览

您可以使用RemoteViews而无需 Glance。以下示例加载 XML 小部件布局资源并将其设置为预览。setWidgetPreview需要 35 或更高的 compileSdk 构建设置才能在此代码段中显示为方法。

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>
An image showing a widget preview
图 3. 默认情况下出现在 3x3 区域中但由于其 XML 布局而可以适应 3x1 区域的小部件预览。

要显示准确的预览,您可以通过完成以下步骤直接提供实际的小部件布局和默认值

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

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

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

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

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

要让 Android 11(API 级别 30)或更低版本上的小部件选择器显示小部件的预览,请指定previewImage属性。

如果更改小部件的外观,请更新预览图像。

为您的窗口小部件添加名称

小部件在小部件选择器中显示时需要具有唯一名称。

小部件的名称加载自 AndroidManifest.xml 文件中小部件 receiver 元素的 label 属性。

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

为您的小部件添加描述

从 Android 12 开始,请为小部件选择器提供要显示的小部件描述。

An image showing a widget picker showing a widget and its description
图 4. 显示小部件及其描述的示例小部件选择器。

使用 &lt;appwidget-provider&gt; 元素的 description 属性为您的 Widget 提供描述。

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