Android- Widget (應(yīng)用微件/小組件/插件) 使用介紹

一、概念:

App Widget 即叫 應(yīng)用微件 或者 小組件/插件.
是可以嵌入其他應(yīng)用(如主屏幕)并 接收定期更新微型應(yīng)用視圖。
這些視圖稱為界面中的微件.
例如,添加到桌面上的音樂Widget:

app_widget_music_sample.PNG

能夠容納其他應(yīng)用微件的應(yīng)用組件稱為 AppWidgetHost (應(yīng)用微件托管應(yīng)用)。
App 通過傳遞要顯示布局id 給RemoteViews, 即可以獲取Widget的實(shí)例對象.

官網(wǎng)介紹:
指南:https://developer.android.com/guide/topics/appwidgets
UI指南(更詳細(xì)): https://developer.android.com/develop/ui/views/appwidgets/overview

二、基礎(chǔ)知識

官網(wǎng)上的介紹順序有點(diǎn)難懂,可以參考一下順序,方便理解.

要?jiǎng)?chuàng)建App Widget,大體需要以下三點(diǎn):

(1) 視圖布局
定義 Widget的布局 (xml)

注意:
這個(gè)布局需要添加到 RemoteViews, 而 RemoteViews 是不支持 ConstraintLayout的.
支持:LinearLayout/FrameLayout/RelativeLayout/GridLayout, 以及TextView/Button等基礎(chǔ)控件(不支持他們的子類)

(2) AppWidgetProviderInfo 對象
將數(shù)據(jù)封裝在元素<appwidget-provider>里
描述App Widget的元數(shù)據(jù)(meta-data),如布局、更新頻率和 AppWidgetProvider 類。
此對象應(yīng)在 XML 中定義 并且 要在 AndroidManifest.xml中聲明。

(3) AppWidgetProvider 類實(shí)現(xiàn)
定義了 基于廣播事件, 通知到 Widget 的方法。
會在更新、啟用、停用和刪除 Widget 時(shí)收到廣播。
要在 AndroidManifest.xml中聲明。
App可以通過它操作RemoteViews,例如更新等

三、使用流程

1. 創(chuàng)建Widget 內(nèi)容的 布局

/layout/widget_content.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/forward_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="這是一個(gè)測試的Widget內(nèi)容 點(diǎn)擊我快速使用 App 某個(gè)功能" />

</RelativeLayout>

2. 添加 AppWidgetProviderInfo 元數(shù)據(jù)

數(shù)據(jù)封裝在元素<appwidget-provider>里.
/xml/example_appwidget_info.xml

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialKeyguardLayout="@layout/widget_content"
    android:initialLayout="@layout/widget_content"
    android:minHeight="110dp"
    android:minWidth="180dp"
    android:previewImage="@drawable/example_appwidget_preview"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="0"
    android:widgetCategory="home_screen">

</appwidget-provider>

其中,
(1) initialLayout 則表示 widget 顯示的內(nèi)容
(2) minWidth / minHeight 表示W(wǎng)idget默認(rèn)情況下,占用的最小空間. (最小大小不得超過 4 x 4 單元格)
(3) previewImage 表示預(yù)覽圖片,即用戶添加時(shí)展示的 (這里是example_appwidget_preview.png )
(4) resizeMode 指定可以按什么規(guī)則來調(diào)整大小 (“horizontal”、“vertical”和“none”)
(5) widgetCategory 是否可以顯示在主屏幕(home_screen) 和/或鎖定屏幕 (keyguard)
注:這個(gè)XML 文件將會在AndroidManifest 聲明 AppWidgetProvider組件時(shí),由meta-data引用

3. 使用 AppWidgetProvider 類

AppWidgetProvider 類擴(kuò)展了 BroadcastReceiver, 作為一個(gè)輔助類來處理應(yīng)用微件廣播。
僅接收與Widget有關(guān)的事件廣播,例如當(dāng)更新、刪除、啟用和停用Widget時(shí)發(fā)出的廣播.

當(dāng)發(fā)生這些廣播事件時(shí),AppWidgetProvider 會接收以下方法調(diào)用:
onUpdate()
onAppWidgetOptionsChanged()
onDeleted(Context, int[])
onEnabled(Context)
onDisabled(Context)
onReceive(Context, Intent)

最重要的是 onUpdate, 如果要處理任何用戶交互事件,都需要在此回調(diào)處理.
這里我們設(shè)置,點(diǎn)擊了Widget 后,跳轉(zhuǎn)到 ExampleActivity 里.

class ExampleAppWidgetProvider : AppWidgetProvider() {

    override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray
    ) {
        // Perform this loop procedure for each App Widget that belongs to this provider
        appWidgetIds.forEach { appWidgetId ->
            // Create an Intent to launch ExampleActivity
            val pendingIntent: PendingIntent = Intent(context, ExampleActivity::class.java)
                .let { intent ->
                    PendingIntent.getActivity(context, 0, intent,
                        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) // set flags to fix fatal when sdk >=31
                }

            // Get the layout for the App Widget and attach an on-click listener
            // to the button
            val views: RemoteViews = RemoteViews(
                context.packageName,
                R.layout.widget_content
            ).apply {
                setOnClickPendingIntent(R.id.forward_button, pendingIntent)
            }

            // Tell the AppWidgetManager to perform an update on the current app widget
            appWidgetManager.updateAppWidget(appWidgetId, views)
        }
    }
}
    

其中,
(1) appWidgetIds 是一個(gè)ID 數(shù)組,對應(yīng)每一添加到主屏幕的Widget實(shí)例.
(即:可以在主屏幕添加 多個(gè)Widget)
(2) 不同的Widget 實(shí)例,updatePeriodMillis 更新周期表依照第一個(gè)Widget實(shí)例.
(3) pendingIntent 用于點(diǎn)擊Widget上的Button時(shí),跳轉(zhuǎn)到ExampleActivity
注意:在SDK>=31 上,需要設(shè)置 FLAG_IMMUTABLE. 否則會出現(xiàn)fatal error:Strongly consider using FLAG_IMMUTABLE...
(4) ExampleActivity 是一個(gè)簡單的Activity,
代碼:

class ExampleActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_example)
    }
}

activity_example 為布局資源,僅顯示一個(gè)TextView

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ExampleActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Show this From Widget!!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

4. 在AndroidManifest中聲明 AppWidgetProvider 并聲明 AppWidgetProviderInfo

        <receiver android:name="ExampleAppWidgetProvider"
            android:exported="true">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data android:name="android.appwidget.provider"
                android:resource="@xml/example_appwidget_info" />
        </receiver>

需要在清單中聲明后,才能在小組件設(shè)置里添加.

此處的聲明可以反推出來:
(1) App Widget的入口,其實(shí)依賴 <intent-filter>里聲明的action:android.appwidget.action.APPWIDGET_UPDATE
(2)猜測系統(tǒng)(Launcher) 將會遍歷所有的AndroidManifest, 然后找到有這個(gè)action所在的組件.
在用戶長按應(yīng)用圖標(biāo)時(shí),可顯示應(yīng)用 自定義的組件。
(3)而這個(gè)自定義組件的內(nèi)容是 從 meta-data里的 "android.appwidget.provider" 讀取出來。
(4)后續(xù),有小組件的事件,則是通知到它所聲明的receiver (這里是ExampleAppWidgetProvider)

至此,已經(jīng)完成所有,
運(yùn)行app即可體驗(yàn).

5. 效果圖如下:

(1)添加Widget


demo_add widget.PNG

(2) 添加Widget時(shí)的 預(yù)覽圖


add widget preview.PNG

(3) Widget 顯示圖


widget_in home screen.PNG

(4) 點(diǎn)擊Widget的button 后則會跳轉(zhuǎn)的對應(yīng)的Activity

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容