允许用户配置应用 widget

应用微件可以配置。例如,时钟 widget 可让用户配置要显示的时区。

如果您想允许用户配置 widget 的设置,请创建 widget 配置 Activity。此 activity 在创建 widget 时或之后由应用 widget 宿主自动启动,具体取决于您指定的配置选项

声明配置 activity

在 Android 清单文件中将配置 activity 声明为正常 activity。应用 widget 宿主通过 ACTION_APPWIDGET_CONFIGURE 操作启动它,因此 activity 需要接受此 intent。例如:

<activity android:name=".ExampleAppWidgetConfigurationActivity">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
    </intent-filter>
</activity>

使用 android:configure 属性在 AppWidgetProviderInfo.xml 文件中声明 activity。如需了解详情,请参阅声明此文件。以下示例说明了如何声明配置 activity:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:configure="com.example.android.ExampleAppWidgetConfigurationActivity"
    ... >
</appwidget-provider>

该 activity 是使用完全限定的命名空间声明的,因为启动器会从软件包范围之外引用它。

这就是启动配置 activity 所需的全部内容。接下来,您需要实现实际 activity。

实现配置 activity

实现该 activity 时,需要注意两个要点:

  • 应用 widget 宿主会调用配置 activity,并且配置 activity 必须始终返回结果。结果必须包含由启动 activity 的 intent 传递的应用微件 ID(在 intent extra 中保存为 EXTRA_APPWIDGET_ID)。
  • 系统不会在配置 activity 启动时发送 ACTION_APPWIDGET_UPDATE 广播,这意味着在创建 widget 时它不会调用 onUpdate() 方法。首次创建 widget 时,由配置 activity 负责从 AppWidgetManager 请求更新。不过,系统会针对后续更新调用 onUpdate(),只在第一次更新时才会跳过。

如需查看有关如何从配置返回结果并更新 widget 的示例,请参阅下一部分中的代码段。

通过配置 activity 更新 widget

当某个 widget 使用配置 activity 时,该 activity 负责在配置完成后更新 widget。为此,您可以直接从 AppWidgetManager 请求更新。

下面总结了正确更新 widget 和关闭配置 activity 的过程:

  1. 从启动 activity 的 intent 中获取应用微件 ID:

    Kotlin

    val appWidgetId = intent?.extras?.getInt(
            AppWidgetManager.EXTRA_APPWIDGET_ID,
            AppWidgetManager.INVALID_APPWIDGET_ID
    ) ?: AppWidgetManager.INVALID_APPWIDGET_ID
    

    Java

    Intent intent = getIntent();
    Bundle extras = intent.getExtras();
    int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
    if (extras != null) {
        appWidgetId = extras.getInt(
                AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);
    }
    
  2. 将 activity 结果设置为 RESULT_CANCELED

    这样,如果用户在到达终点之前退出 activity,则系统会通知应用 widget 宿主配置已取消,并且宿主不会添加该 widget:

    Kotlin

    val resultValue = Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
    setResult(Activity.RESULT_CANCELED, resultValue)
    

    Java

    int resultValue = new Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
    setResult(Activity.RESULT_CANCELED, resultValue);
    
  3. 根据用户的偏好设置配置 widget。

  4. 配置完成后,通过调用 getInstance(Context) 获取 AppWidgetManager 的实例:

    Kotlin

    val appWidgetManager = AppWidgetManager.getInstance(context)
    

    Java

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    
  5. 通过调用 updateAppWidget(int,RemoteViews) 来使用 RemoteViews 布局更新该 widget:

    Kotlin

    val views = RemoteViews(context.packageName, R.layout.example_appwidget)
    appWidgetManager.updateAppWidget(appWidgetId, views)
    

    Java

    RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget);
    appWidgetManager.updateAppWidget(appWidgetId, views);
    
  6. 创建返回 intent,使用 activity 结果对其进行设置,然后完成 activity:

    Kotlin

    val resultValue = Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
    setResult(Activity.RESULT_OK, resultValue)
    finish()
    

    Java

    Intent resultValue = new Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
    setResult(RESULT_OK, resultValue);
    finish();
    

如需查看示例,请参阅 GitHub 上的 ListWidgetConfigureActivity.kt 示例类。

微件配置选项

默认情况下,应用 widget 宿主只会在用户将 widget 添加到主屏幕后立即启动配置 activity 一次。不过,您可以通过提供默认 widget 配置来指定相应选项,让用户能够重新配置现有 widget 或跳过初始 widget 配置。

允许用户重新配置已放置的微件

如需允许用户重新配置现有 widget,请在 appwidget-providerwidgetFeatures 属性中指定 reconfigurable 标志。如需了解详情,请参阅有关声明 AppWidgetProviderInfo.xml 文件的指南。例如:

<appwidget-provider
    android:configure="com.myapp.ExampleAppWidgetConfigurationActivity"
    android:widgetFeatures="reconfigurable">
</appwidget-provider>

用户可以重新配置其 widget,方法是轻触并按住该 widget,然后点按重新配置按钮(在图 1 中标记为 1)。

按钮显示在右下角
图 1. 微件重新配置按钮。

使用微件的默认配置

您可以通过允许用户跳过初始配置步骤,提供更顺畅的 widget 体验。为此,请在 widgetFeatures 字段中同时指定 configuration_optionalreconfigurable 标志。这样会在用户添加微件后绕过启动配置 activity。如前所述,用户之后仍然可以重新配置 widget。例如,时钟 widget 可以绕过初始配置并默认显示设备时区。

以下示例展示了如何将配置 activity 同时标记为可重新配置和可选:

<appwidget-provider
    android:configure="com.myapp.ExampleAppWidgetConfigurationActivity"
    android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>

允许用户固定 widget

在搭载 Android 8.0(API 级别 26)及更高版本的设备上,允许用户创建固定快捷方式的启动器还可让用户将 widget 固定到主屏幕上。与固定快捷方式类似,这些固定的 widget 让用户能够访问应用中的特定任务,并且可以直接从应用添加到主屏幕。例如,当用户在天气应用中添加新城市时,您可以提示用户将该城市的天气 widget 添加到他们的主屏幕。

在您的应用中,您可以按照以下步骤创建请求,让系统将某个 widget 固定到受支持的启动器:

  1. 请务必在应用的清单文件中声明微件

  2. 调用 requestPinAppWidget() 方法,如以下代码段所示:

Kotlin

val appWidgetManager = AppWidgetManager.getInstance(context)
val myProvider = ComponentName(context, ExampleAppWidgetProvider::class.java)

if (appWidgetManager.isRequestPinAppWidgetSupported()) {
    // Create the PendingIntent object only if your app needs to be notified
    // when the user chooses to pin the widget. Note that if the pinning
    // operation fails, your app isn't notified. This callback receives the ID
    // of the newly pinned widget (EXTRA_APPWIDGET_ID).
    val successCallback = PendingIntent.getBroadcast(
            /* context = */ context,
            /* requestCode = */ 0,
            /* intent = */ Intent(...),
            /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT)

    appWidgetManager.requestPinAppWidget(myProvider, null, successCallback)
}

Java

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName myProvider = new ComponentName(context, ExampleAppWidgetProvider.class);

if (appWidgetManager.isRequestPinAppWidgetSupported()) {
    // Create the PendingIntent object only if your app needs to be notified
    // when the user chooses to pin the widget. Note that if the pinning
    // operation fails, your app isn't notified. This callback receives the ID
    // of the newly pinned widget (EXTRA_APPWIDGET_ID).
    PendingIntent successCallback = PendingIntent.getBroadcast(
            /* context = */ context,
            /* requestCode = */ 0,
            /* intent = */ new Intent(...),
            /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT);

    appWidgetManager.requestPinAppWidget(myProvider, null, successCallback);
}