Android 12 widgets improvements | Android 12 Developer Preview
source link: https://developer.android.com/about/versions/12/features/widgets
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Android 12 revamps the existing Widgets API to improve the user and developer experience in the platform and launchers. Use this guide to learn how to ensure your widget is compatible with Android 12, and also as a reference for APIs for refreshing your existing widget.
Ensure your widget is compatible with Android 12
Widgets in Android 12 have rounded corners. When an app widget is used on a device running Android 12 or higher, the launcher automatically identifies the widget's background and crops it to have rounded corners.
In this scenario, your widget may not display properly in either of the following conditions:
The widget contains content in the corners: This may cause some content in the corner area to be cropped.
The widget uses a background that is not susceptible to cropping. This includes a transparent background, empty views or layouts, or any other kind of special background not prone to cropping. The system may not be able to correctly identify the background to use.
If your widget will be affected by this change, we recommend refreshing it with rounded corners (as described in the following section) to ensure it displays properly.
Caution: The dimensions of rounded corners may vary across devices because the size of the corner radius is controllable by both device manufacturers (up to 16dp) and third-party launchers. We recommend refreshing the widget to help avoid unsatisfactory results.Use the sample
To see all these new APIs in action, check out our sample list widget.
Implement rounded corners
Android 12 introduces the following system parameters to set the radii of your widget's rounded corners:
system_app_widget_background_radius
: The corner radius of the widget background, which will never be larger than 28dp.system_app_widget_inner_radius
: The corner radius of any view inside the widget. This is exactly 8dp less than the background radius to align nicely when using an 8dp padding.
The following example shows a widget that uses
system_app_widget_background_radius
for the corner of the widget and
system_app_widget_inner_radius
for views inside the widget.
1 Corner of the widget.
2 Corner of a view inside the widget.
Caution: These parameters can be overridden by device manufacturers or third-party launchers that are aiming to tailor experiences.Backward-compatibility with rounded corners
To ensure widget compatibility with previous versions of Android, we recommend defining custom attributes and using a custom theme to override them for Android 12, as shown in the following examples of XML files:
<resources>
<attr name="backgroundRadius" format="dimension" />
</resources>
Apply dynamic colors
In Android 12, a widget can use the device theme colors for buttons, backgrounds, and other components. This enables smoother transitions and consistency across different widgets.
In the following example, the device theme color is “brownish”, causing the
accent color and widget background to adapt. You can achieve this by using the
system's default theme (@android:style/Theme.DeviceDefault.DayNight
) and its
color attributes. Some commonly-used color attributes are:
?android:attr/colorAccent
?android:attr/colorBackground
?android:attr/textColorPrimary
and?android:attr/textColorSecondary
Backward-compatibility with dynamic colors
We recommend creating a custom theme and overriding it for Android 12. The following examples show how to do this with various types of XML files:
<resources>
<style name="MyWidget.TextView">
<item name="android:textColor">@color/my_text_color</item>
</style>
<style name="MyWidgetTheme">
<item name="textViewStyle">@style/MyWidget.TextView</item>
</style>
</resources>
Make it easier to personalize widgets
If you specify a configuration activity with the configure
attribute of appwidget-provider
, the App Widget
host launches that activity immediately after a user adds the widget to their
home screen.
Android 12 adds new options to let you provide a better configuration experience for users.
Enable users to reconfigure placed widgets
To configure widgets that are labeled as reconfigurable, users can long-press the widget. This displays a Reconfigure button, which they can tap to change settings.
1 Reconfigure button.
Specify the reconfigurable
flag in the widgetFeatures
attribute of appwidget-provider
:
<appwidget-provider
...
android:configure="com.myapp.WidgetConfigActivity"
android:widgetFeatures="reconfigurable">
</appwidget-provider>
Note: The reconfigurable
flag was introduced in Android 9 (API level 28), but
it was not widely supported in launchers until Android 12.Use the widget's default configuration
If you’d like your widget to use its default configuration when a user adds it,
you can skip the configuration step by specifying both the
configuration_optional
and reconfigurable
flags in the widgetFeatures
field. This bypasses
launching the configuration activity after a user adds the widget. (As mentioned
previously, the user can still reconfigure the widget
afterwards.)
For example, a clock widget could bypass the initial configuration and show the device time zone by default.
<appwidget-provider
...
android:configure="com.myapp.WidgetConfigActivity"
android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>
Backward-compatibility with widget configuration options
Your app can use the configuration_optional
and reconfigurable
flags in
previous versions of Android. However, these flags won't have any effect and the
system would still launches the configuration activity.
Add new compound buttons
Android 12 adds new support for stateful behavior using the following existing components:
The widget is still stateless. Your app must store the state and register for state change events.
The following code example shows how to implement these components.
// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true)
// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2)
// Listen for check changes. The intent will have an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
R.id.my_checkbox,
RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent)
)
Backward-compatibility with widget compound buttons
Provide two different layouts, with one targeting devices running Android 12 or
higher (res/layout-v31
) and the other targeting previous versions of Android
(in the default res/layout
folder).
Use improved APIs for widget sizes and layouts
Starting in Android 12, you can provide more refined size attributes and more flexible layouts and by doing the following:
Specify additional widget sizing constraints
Android 12 adds new APIs allowing you to ensure your widget is sized more reliably across different devices with varying screen sizes.
In addition to the existing minWidth
,
minHeight
,
minResizeWidth
,
and minResizeHeight
attributes, use the following new appwidget-provider
attributes:
targetCellWidth
andtargetCellHeight
: Defines the target size of the widget in terms of launcher grid cells. If defined, these attributes are used instead ofminWidth
orminHeight
.maxResizeWidth
andmaxResizeHeight
: Defines the maximum size the launcher allows the user to resize the widget.
The following XML describes how to use the sizing attributes.
<appwidget-provider
...
android:targetCellWidth="3"
android:targetCellHeight="2"
android:maxResizeWidth="250dp"
android:maxResizeHeight="110dp">
</appwidget-provider>
Provide responsive layouts
If the layout needs to change depending on the size of the widget, we recommend creating a small set of layouts, each valid for a range of sizes. (If this isn’t possible, another option is to provide layouts based on the exact widget size at runtime.)
Implementing this feature allows for smoother scaling and overall better system health; this is because the system doesn't have to wake up the app every time it displays the widget in a different size.
The following code example shows how to provide a list of layouts.
override fun onUpdate(...) {
val smallView = ...
val tallView = ...
val wideView = ...
val viewMapping: Map<SizeF, RemoteViews> = mapOf(
SizeF(100f, 100f) to smallView,
SizeF(100f, 200f) to tallView,
SizeF(200f, 100f) to wideView
)
val remoteViews = RemoteViews(viewMapping)
appWidgetManager.updateAppWidget(id, remoteViews)
}
Provide exact layouts
If a small set of responsive layouts isn't feasible, you can instead provide different layouts tailored to the sizes at which the widget is shown. This is typically two sizes for phones (portrait and landscape mode) and four sizes for foldables.
To implement this solution, your app needs to perform the following steps:
Note: Providing the list of sizes is the launcher’s responsibility. If the device has a launcher that doesn't support that field, the list of sizes might be empty or null.
The following code example shows how to provide exact layouts.
// Create the RemoteViews for the given size.
private fun createRemoteViews(size: SizeF): RemoteViews { }
override fun onAppWidgetOptionsChanged(
context: Context,
manager: AppWidgetManager,
id: Int,
newOptions: Bundle?
) {
super.onAppWidgetOptionsChanged(context, manager, id, newOptions)
// Get the new sizes.
val sizes = newOptions?.getParcelableArrayList<SizeF>(
AppWidgetManager.OPTION_APPWIDGET_SIZES
)
// Check that the list of sizes is provided by the launcher.
if (sizes.isNullOrEmpty()) {
return
}
// Map the sizes to the desired RemoteViews
val remoteViews = RemoteViews(sizes.associateWith(::createRemoteViews))
appWidgetManager.updateAppWidget(id, remoteViews)
}
Backward-compatibility with widget layout sizes
Previously, it was possible to get the widget's size ranges using the
OPTION_APPWIDGET_MIN_WIDTH
,
OPTION_APPWIDGET_MIN_HEIGHT
,
OPTION_APPWIDGET_MAX_WIDTH
,
and OPTION_APPWIDGET_MAX_HEIGHT
extras and estimate the widget's size, but that logic doesn't work in all
situations. For widgets targeting Android 12, we recommend switching to
providing responsive or exact
layouts as explained previously.
Improve your app's widget picker experience
Android 12 enables you to improve the widget picker experience for your app by adding dynamic widget previews and widget descriptions.
Add scalable widget previews to the widget picker
In Android 12, the widget preview displayed in the widget picker consists of a scalable preview, which you’ll provide as an XML layout set to the widget's default size. Previously, the widget preview was a static drawable resource, in some cases leading to previews not accurately reflecting widgets after they were added to the home screen.
To implement scalable widget previews, use the previewLayout
attribute of the appwidget-provider
element to provide an XML layout instead:
<appwidget-provider
...
android:previewLayout="@layout/my_widget_preview">
</appwidget-provider>
Ideally, this should be the same layout as the actual widget with realistic default or test values.
Backward-compatibility with scalable widget previews
To enable widget pickers on Android 11 or lower to show previews of your widget,
continue specifying the previewImage
attribute.
If you make changes to the widget’s appearance, make sure to also update the preview image.
Add a description for your widget
In Android 12, you can optionally provide a description for the widget picker to display for your widget.
Provide a description for your widget using the description attribute of
appwidget-provider
:
<appwidget-provider
...
android:description="@string/my_widget_description">
</appwidget-provider>
Note: There is no character limit, but the representation and available
space for the description may differ depending on the device. It’s
important to be as concise as possible.Backward-compatibility with widget descriptions
Your app can use the widgetDescription
attribute on previous versions of
Android, but it won’t be shown in the widget picker.
Enable smoother transitions
In Android 12, launchers provide a smoother transition when a user launches your app from a widget.
To enable this improved transition, use @android:id/background
or
android.R.id.background
to identify your background element:
// Top level layout of the widget.
<LinearLayout
...
android:id="@android:id/background">
</LinearLayout>
Warning: Avoid using broadcast
trampolines.
In Android 12, an app can still launch an activity from a broadcast
receiver or service if it’s initiated from a widget click’s
PendingIntent
. However, the
new app animation won't be used for apps launched from a broadcast
receiver or service, which leads to a poor user experience.Backward-compatibility with smoother transitions
Your app can use @android:id/background
in previous versions of Android, but
it won’t have any effect.
Use simplified RemoteViews collections
Android 12 adds the setRemoteAdapter(int viewId, RemoteViews.RemoteCollectionItems items)
method, which lets your app pass along a collection directly when populating a
ListView
. Previously, when using a
ListView
, it was necessary to implement and declare a
RemoteViewsService
to return RemoteViewsFactory
.
If the collection doesn’t use a constant set of layouts (in other words, if some
items are only sometimes present), use setViewTypeCount
to specify the maximum
number of unique layouts the collection can contain.
Here’s an example of how to implement simplified RemoteViews
collections.
remoteView.setRemoteAdapter(
R.id.list_view,
RemoteViews.RemoteCollectionItems.Builder()
.addItem(/* id= */ ID_1, RemoteViews(...))
.addItem(/* id= */ ID_2, RemoteViews(...))
...
.setViewTypeCount(MAX_NUM_DIFFERENT_REMOTE_VIEWS_LAYOUTS)
.build()
)
Use runtime modification of RemoteViews
Android 12 adds several RemoteViews
methods that allow for runtime
modification of RemoteViews
attributes. See the RemoteViews
API reference
for the full list of added methods.
The following code example shows how to use a few of the new methods.
// 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)
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK