
很長一段時間來,微件 (widget) 一直是 Android 用戶體驗的核心組成部分,很多應(yīng)用通過微件來提升用戶黏度。用戶樂于使用微件的原因是可以在不打開應(yīng)用的情況下使用應(yīng)用功能,且可自定義設(shè)備的主屏幕。
Android 12 更新了已有的 Widget API,重塑了微件的設(shè)計來契合 "Material You" 設(shè)計語言。這些更新可以幫助您使用設(shè)備的主題顏色和圓角來構(gòu)建更加美觀的微件,從而提升微件在搜索和擺放時的可發(fā)現(xiàn)性和視覺觀感。

在這個系列中,我們將帶您更新微件來適配 Android 12。在本文中我們將進行一些簡單的修改,使您的微件能夠在 Android 12 的設(shè)備上看起來更加精致,且在較舊版本的設(shè)備中提供一致的用戶體驗。在第二篇文章中,我們將了解新的 API,通過它們可以使微件更加個性化、響應(yīng)更靈敏并且更具互動性。
視覺變化
對于用戶來說,毫無疑問最直觀的視覺變化是風(fēng)格和設(shè)計上的改變。更新可視元素,比如顏色和圓角,呈現(xiàn)出的外觀會令用戶耳目一新。增加這些修改,我們推薦您創(chuàng)建一個自定義的主題。
增加動態(tài)顏色
Material You 旨在提供更加個性化的用戶體驗。在 Android 12 中,動態(tài)顏色可以使您的微件與其它微件以及系統(tǒng)保持一致的風(fēng)格。微件可以使用系統(tǒng)默認的主題 Theme.DeviceDefault.DayNight,并且在微件的 UI 元素中使用主題顏色屬性。查看 Material Design 更新一覽 視頻了解更多詳情。
對于 SDK 級別低于 31 的設(shè)備,您需要創(chuàng)建一個繼承自 DeviceDefault 的自定義主題。
values/themes.xml
<style name="Theme.AppWidget.AppWidgetContainer"
parent="@android:style/Theme.DeviceDefault" />
對于 SDK 級別為 31 的設(shè)備,使用主題 DeviceDefault.DayNight 來創(chuàng)建自定義主題。
values-v31/themes.xml
<style name="Theme.AppWidget.AppWidgetContainer"
parent="@android:style/Theme.DeviceDefault.DayNight" />
或者,如果您的應(yīng)用使用了 Material Components,您可以使用
Theme.MaterialComponents.DayNight作為基礎(chǔ)主題,而不是使用Theme.DeviceDefault。
為了能夠讓您的微件可以動態(tài)適配系統(tǒng)顏色,您可以將該主題配置到您的微件上,并且在微件的其它視圖上使用主題顏色屬性。
layout/widget_checkbox_list_title_region.xml
...
<TextView android:id="@+id/checkbox_list_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:layout_weight="1"
android:text="@string/grocery_list"
android:textColor="?android:attr/textColorPrimary" />
<ImageButton
android:layout_width="@dimen/widget_element_min_length"
android:layout_height="@dimen/widget_element_min_length"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:contentDescription="@string/add_button_grocery_list_content_description"
android:src="@drawable/ic_add_24"
android:tint="?android:attr/colorAccent" />
...

圓角
從 Android 12 開始,圓角將自動應(yīng)用于微件。這也意味著圓角會裁剪微件的部分內(nèi)容。為了避免出現(xiàn)這樣的問題,并且提供與其它微件和系統(tǒng)風(fēng)格一致的外觀和用戶體驗,您可以使用 system_app_widget_background_radius 在微件的背景添加圓角,使用 system_app_widget_inner_radius 在微件中的視圖添加圓角。后者的值需要比 system_app_widget_background_radius 小 8dp。
在添加上述修改時,請注意如果您的微件包含靠近角區(qū)域的內(nèi)容,這些內(nèi)容可能會被裁減掉。要解決該問題,您需要添加足夠大的 padding 來避免微件的內(nèi)容與圓角之間的沖突。
values/attrs.xml
<declare-styleable name="AppWidgetAttrs">
<attr name="appWidgetPadding" format="dimension" />
<attr name="appWidgetInnerRadius" format="dimension" />
<attr name="appWidgetRadius" format="dimension" />
</declare-styleable>
values/themes.xml
<style name="Theme.AppWidget.AppWidgetContainerParent"
parent="@android:style/Theme.DeviceDefault">
<!-- 微件的外輪廓的圓角半徑 -->
<item name="appWidgetRadius">16dp</item>
<!-- widget 內(nèi)部視圖邊緣的圓角半徑。它的值為 8 dp 或者小于 appWidgetRadius -->
<item name="appWidgetInnerRadius">8dp</item>
</style>
<style name="Theme.AppWidget.AppWidgetContainer"
parent="Theme.AppWidget.AppWidgetContainerParent">
<!-- 增加 padding 來避免微件的內(nèi)容與圓角沖突 -->
<item name="appWidgetPadding">16dp</item>
</style>
values-v31/themes.xml
<style name="Theme.AppWidget.AppWidgetContainerParent"
parent="@android:style/Theme.DeviceDefault.DayNight">
<item name="appWidgetRadius">
@android:dimen/system_app_widget_background_radius</item>
<item name="appWidgetInnerRadius">
@android:dimen/system_app_widget_inner_radius</item>
</style>
values/styles.xml
<style name="Widget.AppWidget.AppWidget.Container"
parent="android:Widget">
<item name="android:id">@android:id/background</item>
<item name="android:background">
?android:attr/colorBackground</item>
</style>
如果您的
minTargetSDK小于 21,那么您需要提供適用于 SDK 版本 21 的 style,因為在 可繪制對象 上使用android:attr/colorBackground需要 SDK 版本至少為 21。
至此您已經(jīng)創(chuàng)建了主題,現(xiàn)在可以在微件的布局上設(shè)置樣式了。
layout/widget_grocery_list.xml
<LinearLayout
style="@style/Widget.AppWidget.AppWidget.Container">
...
</LinearLayout>

過渡
當應(yīng)用通過微件打開時,Android 12 提供了過渡效果。該過渡效果是由系統(tǒng)自動處理的,并且在舊版本的 Android 上不會出現(xiàn)。要啟用該效果,您需要在微件布局根元素上指定一個 id,并設(shè)置它的值為 android:id/background。
<LinearLayout
android:id="@android:id/background">
...
</LinearLayout>

如果您的微件使用了 broadcast trampoline,也就是說您的微件在用戶點擊時創(chuàng)建了 PendingIntent,通過廣播或者服務(wù)啟動 Activity,那么在這種情況下,該過渡動畫不會生效。
微件選擇器的優(yōu)化
預(yù)覽
Android 12 包含新的經(jīng)過改進的微件選擇器。與使用靜態(tài)可繪制資源不同,新的微件選擇器使用 XML 布局來動態(tài)創(chuàng)建縮放的微件預(yù)覽。
如果您的微件并不包含動態(tài)元素,比如 ListView 或者 GridView,您可以使用微件的布局實現(xiàn)預(yù)覽。
要實現(xiàn)預(yù)覽,您需要將默認值直接設(shè)置到原始布局上。
<TextView
style="@style/Widget.AppWidget.Checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/widget_title_preview" />
<TextView
style="@style/Widget.AppWidget.Checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/widget_subject_preview" />
在布局上設(shè)置默認值可能會帶來少量的延遲,因為占位的值會在實際值之前首先被啟用。要避免該問題,您可以為預(yù)覽創(chuàng)建一個獨立的布局文件,并且啟用自定義的預(yù)覽主題。
<resources>
<!-- 聲明屬性-->
<attr name="widgetTitlePreview" format="string" />
<attr name="widgetSubjectPreview" format="string" />
<!-- 聲明 style -->
<style name="Theme.MyApp.Widget"
parent="@style/Theme.DeviceDefault.DayNight.AppWidget">
<item name="widgetTitlePreview"></item>
<item name="widgetSubjectPreview"></item>
</style>
<style name="Theme.MyApp.Widget.Preview">
<item name="widgetTitlePreview">Preview Title</item>
<item name="widgetSubjectPreview">Preview Subject</item>
</style>
</resources>
創(chuàng)建預(yù)覽主題后,您可以在布局中將它應(yīng)用到預(yù)覽元素上。
layout/my_widget_preview.xml
<LinearLayout ...>
<include layout="@layout/widget_header"
android:theme=”@style/Theme.MyApp.Widget.Preview” /></LinearLayout>
layout/my_widget_actual.xml
<LinearLayout ...>
<include layout="@layout/widget_header"
android:theme=”@style/Theme.MyApp.Widget” />
</LinearLayout>
最后,您需要將微件的布局設(shè)置為 appwidget-provider 的 previewLayout 屬性。
xml/app_widget_info_checkbox_list.xml
<appwidget-provider
android:previewLayout="@layout/widget_grocery_list"
...
/>

對于顯示多個元素的 ListView、GridView 或者 Stack,是無法直接在布局上設(shè)置默認值的。對于這些視圖,您可以為微件預(yù)覽創(chuàng)建另一個布局,并且在布局中設(shè)置固定的值。
要實現(xiàn)上述操作,推薦的最佳實踐是使用 <include> 標簽來復(fù)用布局的一部分以啟用默認值,而無需復(fù)制整個布局。您可以將新的布局設(shè)置為 appwidget-provider 的 previewLayout 屬性。
描述
您也可以設(shè)置 description 屬性作為描述信息顯示在微件選擇器上。雖然這是可選項,但是提供描述信息可以幫助用戶更好地了解微件的功能。
app_widget_info_checkbox_list.xml
<appwidget-provider
android:description="@string/app_widget_grocery_list_description"
...
/>

總結(jié)
在本文中,我們?yōu)槟故玖巳绾胃挛⒓O(shè)計并且在微件選擇器中提供更好的用戶體驗。上述內(nèi)容可以快速更新您的微件來適配 Android 12,您的用戶可以看到非常直觀的區(qū)別。
但這并不是全部。在下一篇文章中,我們將會了解新的 API,它可以使您的微件更加個性化,響應(yīng)更靈敏且更具互動性。
歡迎您 點擊這里 向我們提交反饋,或分享您喜歡的內(nèi)容、發(fā)現(xiàn)的問題。您的反饋對我們非常重要,感謝您的支持!