头图

This article is the second article in the "Update Android 12 widgets" series. In the part of we explored some simple methods to present a very dominant visual update for APP users. In this article, we will learn about some higher-level features together that will make your widget more interactive, easier to configure, and present a better UI experience on Android 12.

Simpler configuration

Before Android 12, resetting the widget meant that the user had to delete the existing widget and then add it again with the new configuration. Android 12 has improved the configuration of widgets in many aspects, thereby helping users to personalize widgets in a simpler way.

users can reset the original widget

The reconfigurable widget allows users to customize the widget settings. In Android 12, users will not need to delete and re-add widgets to adjust these original settings.

To use this function, you need to set the widgetFeatures property to reconfigurable appwidget-provider

xml/app_widget_info_checkbox_list.xml
<appwidget-provider
   android:configure="com.example.android.appwidget.ListWidgetConfigureActivity"
   android:widgetFeatures="reconfigurable"
   ... />

Default configuration

If your widget relies on the default settings, you can skip the initialization operation in Android 12 and set the widget through the default configuration.

Let's take a look at how the example widget works. In this use case, we want users to be able to choose between two different widget layouts, namely Grocery List and To-Do List. We will set Grocery List as the default setting so that users do not need to perform configuration steps unless they want to switch to To-Do List.

To achieve this use case, you can store user options and return the Grocery List as the default value without making a selection operation.

ListAppWidget.kt
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)

At this point, the widget has been set to "provide default configuration", and you need to set the configuration_optional flag to the widgetFeatures attribute. This operation will skip the extra configuration step, and you can directly present the widget on the user's home screen. At the same time, please make sure to add the reconfigurable flag so that the user can change the effective default configuration later.

xml/app_widget_info_checkbox_list.xml
<appwidget-provider
   android:configure="com.example.android.appwidget.ListWidgetConfigureActivity"
   android:widgetFeatures="reconfigurable|configuration_optional"
   ... />

Based on this change, when a user adds a widget to the home screen, the widget will automatically enable the Grocery List layout. As we add configuration activities to appwidget-provider of configure attribute, when users press widget and click Edit / Reset button, the configuration will take effect.

When the user configures the widget, the new configuration will be recorded in ListWidgetConfigureActivity .

ListWidgetConfigureActivity.kt
private fun onWidgetContainerClicked(@LayoutRes widgetLayoutResId: Int) {
   ListSharedPrefsUtil.saveWidgetLayoutIdPref(this, appWidgetId, widgetLayoutResId)
   // 配置活动有责任更新 app widget
   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()
}

New & Improved API

When it comes to Android system devices, users have a lot of choices, whether it's mobile phones, tablets, foldable devices, or other types of products. Android 12 introduces complete size attributes and more flexible layouts, which makes widgets easier to customize, and has stable and reliable performance on different devices and screen sizes.

Widget size limit

In addition to the existing minWidth , minHeigh , minResizeWidth and minResizeHeight , Android 12 also adds a new appwidget-provider attribute.

You can use the new maxResizeWidth and maxResizeHeight attributes to define the maximum height and width of the widget size that users can adjust. The new targetCellWidth and targetCellHeight attributes can define the default size of the widget on the main screen of the device.

When the targetCellWidth and targetCellHeight attributes are defined, devices equipped with Android 12 will use these attributes instead of minWidth and minHeight . Devices with Android 11 and below will continue to use the minWidth and minHeight attributes.

Tip: targetCellWidth and targetCellHeight attributes defined in the cells, whereas maxResizeWidth and maxResizeHeight properties are defined in the dps.
xml/app_widget_info_checkbox_list.xml
<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"
   ... />

Responsive layout

Although the size restriction can help users adjust the size of the widget according to their own needs, you may prefer to provide different layouts and content types based on the size of the widget. This also enables the system to display widgets of different sizes without waking up the application.

To do this, you first need to create a set of layouts of different sizes, and then call the updateAppWidget() function and pass in a set of layouts (as shown in the figure below). When the widget size changes, the system will automatically change the layout.

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))
//...

Compound button

On Android 12, users can do more things with widgets without launching the app! With the new compound button, you can make the widget more interactive. This does not change the stateless nature of the widget, but you can add a listener to observe the state changes. You can call RemoteResponse.fromPendingIntent() PendingIntent to the listener when the state changes.

ItemsCollectionAppWidget.kt
remoteViews.setOnCheckedChangeResponse(
   R.id.item_switch,
   RemoteViews.RemoteResponse.fromPendingIntent(
       onCheckedChangePendingIntent
   )
)

On the other hand, if the widget has a list of controls, it is not recommended that you set PendingIntent on the single-item collection, because this will cause poor performance. In this case, you can set a PendingIntent template on the collection, call up RemoteResponse.fromFillInIntent() fillInIntent to the listener when the status changes.

ItemsCollectionAppWidget.kt
remoteViews.setOnCheckedChangeResponse(
   R.id.item_switch,
   RemoteViews.RemoteResponse.fromFillInIntent(
       onCheckedChangeFillInIntent
   )
)

simplified version of RemoteViews in the Collection

Android 12 introduces a new API that can simplify the process of sending a single collection to populate the widget list. Before that, if you want to fill ListView , GridView , StackView or other views through the collection of items, you need to perform the action of RemoteViewsService returning RemoteViewsFactory With the new setRemoteAdapter() API, you can easily use the core RemoteView to send collections. We are also doing the return work of androidx to ensure that the API is still valid on the old Android version.

If the collection does not use constant setting layouts, you can use the setViewTypeCount() function to set the maximum layout ID that will be used by RemoteView

ItemsCollectionAppWidget.kt
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()
)

Conclusion

Please update your existing widgets to Android 12! You will experience a widget with a new look and more interaction.

Now that you have learned about the configurable, new or improved API in this article, please refer to our previous tweet " update your widget to adapt to Android 12 " to learn about the updated widget design and Provide a better user experience method in widget picker. If you need to go further, please refer to the code sample mentioned in the article.

If you are constructing a new widget, please pay attention to subsequent releases. In order to make it easier to construct a new widget, we have been working hard!

You are welcome to click here to submit feedback to us, or share your favorite content, problems found. Your feedback is very important to us, thank you for your support!


Android开发者
404 声望2k 粉丝

Android 最新开发技术更新,包括 Kotlin、Android Studio、Jetpack 和 Android 最新系统技术特性分享。更多内容,请关注 官方 Android 开发者文档。