原文链接。
在 Android 12 中使用你的微件做更多事情!
这篇文章是我写的关于更新 Android 12 微件的小连载文章的第二篇。在上一部分中,我们探索了一些简单的方法来实现对app用户高度可见的可视化更新。在本部分中,我们将介绍一些更高级的功能,使您的微件更具交互性,更易于配置,并在 Android 12 上提供更好的UI体验。
更简单的配置
在 Android 12 之前,重新配置微件意味着用户必须删除现有的微件,并使用新配置再次添加。Android 12 以多种方式改进了微件的配置方式,并帮助用户更轻松地个性化微件。
允许用户重新配置已放置的微件
可重新配置的微件允许用户根据其所需设置自定义微件。使用 Android 12,他们不再需要删除和重新添加微件来调整这些设置。
您可以调整微件的大小并重新配置其设置
要启用此功能,请在 appwidget-provider
中将 widgetFeatures
属性设置为 reconfigurable
。
xml/app_widget_info_checkbox_list.xml
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
<appwidget-provider
android:configure="com.example.android.appwidget.ListWidgetConfigureActivity"
android:widgetFeatures="reconfigurable"
... />
默认配置
如果您的微件可以依赖默认设置,您可以跳过启动初始配置 activity,并在 Android 12 中使用默认配置设置微件。
让我们概览下示例微件,看看它是如何工作的。在这个用例中,我们希望用户能够在两个不同的微件布局中进行选择,杂货店列表和待办事项列表。我们希望使用杂货店列表作为默认设置,因此用户不需要执行配置步骤,除非他们想切换到待办事项列表。
为了实现这个用例,您可以存储用户的选择,并在没有事先选择的情况下返回杂货店清单作为默认值。
ListAppWidget.kt
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
val layoutId = ListSharedPrefsUtil.loadWidgetLayoutIdPref(
context, appWidgetId
)
val remoteViews = if (layoutId == R.layout.widget_grocery_list) {
// 使用dp指定你想用于指定布局尺寸的最大宽度和高度
val viewMapping = mapOf(
SizeF(150f, 150f) to constructRemoteViews(
R.layout.widget_grocery_list
), SizeF(250f, 150f) to constructRemoteViews(
R.layout.widget_grocery_grid
)
)
RemoteViews(viewMapping)
} else {
constructRemoteViews(
layoutId
)
}
appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
现在,微件已配置为提供默认配置,您需要将 configuration_optional
标志设置给 widgetFeatures
属性。这将跳过额外的配置步骤,并将微件直接放在用户的主屏幕上。执行此操作时,请确保还添加了 reconfigurable
标志,以便用户可以在以后更改已应用的默认设置。
xml/app_widget_info_checkbox_list.xml
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
<appwidget-provider
android:configure="com.example.android.appwidget.ListWidgetConfigureActivity"
android:widgetFeatures="reconfigurable|configuration_optional"
... />
通过此更改,当用户将微件添加到主屏幕时,它会自动使用杂货店列表布局。由于配置 activity 被添加到 appwidget-provider
的 configure
属性中,因此当用户长按该微件并单击编辑/重新配置按钮时,activity 将启动。
当用户单击编辑/重新配置按钮时,将启动 ListWidgetConfigureActivity
当用户配置微件时,此新配置存储在 ListWidgetConfigureActivity
中。
ListWidgetConfigureActivity.kt
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
private fun onWidgetContainerClicked(@LayoutRes widgetLayoutResId: Int) {
ListSharedPrefsUtil.saveWidgetLayoutIdPref(this, appWidgetId, widgetLayoutResId)
// 配置 activity 负责更新 app 的微件
val appWidgetManager = AppWidgetManager.getInstance(this)
ListAppWidget.updateAppWidget(this, appWidgetManager, appWidgetId)
// 确保我们传回原始的 appWidgetId
val resultValue = Intent()
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
setResult(RESULT_OK, resultValue)
finish()
}
新的和改进的 API
当谈到运行 Android 的设备时,有许多不同的外形因素可供选择,无论是手机、平板电脑、折叠式还是其他类型的产品。Android 12 引入了精细的尺寸属性和更灵活的布局,使微件更易于定制,在不同的设备和屏幕尺寸下更可靠。
微件大小限制
除了现有的 minWidth
、minHeight
、minResizeWidth
和 minResizeHeight
之外,Android 12 还添加了新的 appwidget-provider
属性。
您可以使用新的 maxResizeWidth
和 maxResizeHeight
属性定义用户可以调整微件大小的最大高度和宽度。新的 targetCellWidth
和 targetCellHeight
属性定义设备主屏幕上的默认微件大小。
定义 targetCellWidth
和 targetCellHeight
属性后,运行 Android 12 的设备将使用这些属性,而不是 minWidth
和 minHeight
。运行 Android 11 及更低版本的设备将继续使用 minWidth
和 minHeight
属性。
注意:
targetCellWidth
和targetCellHeight
属性用单元格定义,而maxResizeWidth
和maxResizeHeight
属性用 dp 定义。
xml/app_widget_info_checkbox_list.xml
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
<appwidget-provider
android:maxResizeWidth="240dp"
android:maxResizeHeight="180dp"
android:minWidth="180dp"
android:minHeight="110dp"
android:minResizeWidth="180dp"
android:minResizeHeight="110dp"
android:targetCellWidth="3"
android:targetCellHeight="2"
... />
在Android 12中,使用 targetCellWidth
和 targetCellHeight
属性代替 minWidth
和 minHeight
响应式布局
尽管使用大小限制可以帮助用户根据需要调整微件的大小,但您可能希望根据微件的大小提供不同的布局和内容类型。这也使系统能够以不同的大小显示微件,而无需唤醒 app。
要实现这一点,首先为一系列尺寸创建一组布局,然后调用 updateAppWidget()
函数并传递布局集合,如下面的示例所示。当微件尺寸更改时,系统会自动更改布局。
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
val viewMapping: MutableMap<SizeF, RemoteViews> = mutableMapOf()
// 使用dp指定你想用于指定布局尺寸的最大宽度和高度
val viewMapping = mapOf(
SizeF(150f, 110f) to RemoteViews(
context.packageName,
R.layout.widget_grocery_list
),
SizeF(250f, 110f) to RemoteViews(
context.packageName,
R.layout.widget_grocery_grid
),
)
appWidgetManager.updateAppWidget(appWidgetId, RemoteViews(viewMapping))
//...
当用户调整微件的大小时,响应式布局提供了更好的可用性
复选按钮
用户无需启动 app,就可以使用 Android 12中 的微件做更多的事情!使用新的复选按钮,您可以使微件更具交互性。这不会改变微件的无状态性质,但允许您添加一个监听器来观察状态更改事件。当发生状态更改事件时,可以调用RemoteResponse.fromPendingIntent()
()并在监听器中传入 PendingIntent
。
ItemsCollectionAppWidget.kt
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
remoteViews.setOnCheckedChangeResponse(
R.id.item_switch,
RemoteViews.RemoteResponse.fromPendingIntent(
onCheckedChangePendingIntent
)
)
另一方面,如果您的微件有一个控件列表,则不应在集合的单个条目上设置 PendingIntent
,因为这会导致性能低下。在这种情况下,在集合上设置一个 PendingIntent
模板,调用 RemoteResponse.fromFillInIntent()
,并在发生状态更改事件时在监听器中传入 fillInIntent。
ItemsCollectionAppWidget.kt
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
remoteViews.setOnCheckedChangeResponse(
R.id.item_switch,
RemoteViews.RemoteResponse.fromFillInIntent(
onCheckedChangeFillInIntent
)
)
微件与复选按钮更具交互性
针对集合简化后的 RemoteViews
Android12 引入了一个新的 API 来简化传递集合以填充微件中的列表的过程。以前,如果要用项目集合填充 ListView
、 GridView
、StackView
或其他 view,则需要实现 RemoteViewsService
以返回 RemoteViewsFactory
。使用新的 setRemoteAdapter()
API,您只需通过主 RemoteView
s 传递一个集合。我们也正在开发 androidx
补丁,以支持在旧版本的 Android 上使用此 API。
如果集合不使用固定的布局集,则可以使用 setViewTypeCount()
函数设置此集合中 RemoteView
s 将使用的最大布局 id 数。
ItemsCollectionAppWidget.kt
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
remoteViews.setRemoteAdapter(
R.id.items_list_view,
RemoteViews.RemoteCollectionItems.Builder()
.addItem(/* id= */ ID_1, RemoteViews(...))
.addItem(/* id= */ ID_2, RemoteViews(...))
//...
.setViewTypeCount(MAX_NUM_DIFFERENT_REMOTE_VIEWS_LAYOUTS)
.build()
)
总结
就这样!将您现有的微件更新到 Android 12 将立即带来更新颖的外观,并使您的微件更具交互性。
既然您已经阅读了本文中有关可配置性和新的或改进的 API 的内容,请查看第一部分,了解如何更新微件的设计,并在微件选择器中提供更好的用户体验。想要了解更多,请查看本文中使用的示例代码。
如果您正在构建新的微件,请密切关注未来的公告。我们正在努力让构建新的微件更加容易!