Android開發筆記[17]-桌面小部件

qsBye發表於2024-03-27

摘要

使用Jetpack Glance庫開發一個詢問"學了嗎"的桌面小部件.

關鍵資訊

  • Android Studio:Iguana | 2023.2.1
  • Gradle:distributionUrl=https://services.gradle.org/distributions/gradle-8.4-bin.zip
  • jvmTarget = '1.8'
  • minSdk 26
  • targetSdk 34
  • compileSdk 34
  • 開發語言:Kotlin,Java
  • ndkVersion = '21.1.6352462'
  • kotlin版本:1.9.20
  • kotlinCompilerExtensionVersion '1.5.4'
  • com.android.library:8.3

原理簡介

Android桌面小部件

[https://developer.android.google.cn/develop/ui/views/appwidgets?hl=zh-cn]
widget 是自定義主螢幕的一個重要方面。您可以將它們視為可透過使用者的主螢幕訪問的應用最重要的資料和功能的“一目瞭然”檢視。使用者可以在其主螢幕皮膚之間移動 widget;如果支援,還可以調整 widget 的大小,以根據他們的偏好定製 widget 中的資訊量。
在規劃 widget 時,請考慮要構建哪種型別的 widget。微件通常屬於以下類別之一:

  1. 資訊微件
    資訊 widget 通常顯示關鍵資訊元素,並跟蹤該資訊隨時間變化的情況。資訊 widget 的示例包括天氣 widget、時鐘 widget 或體育賽事比分跟蹤 widget。點按資訊 widget 通常會啟動關聯的應用,並開啟 widget 資訊的詳細檢視。
  2. 集合微件
    集合 widget 專門用於顯示同一型別的多個元素,例如來自相簿應用中的一組圖片、來自新聞應用的一系列報導,或者來自通訊應用的一系列電子郵件或訊息。集合 widget 可以垂直滾動。
  3. 控制微件
    控制 widget 的主要用途是顯示常用功能,以便使用者無需開啟應用即可從主螢幕觸發這些功能。您可以將它們視為應用的遙控器。控制 widget 的一個示例是家居控制 widget,可讓使用者開啟或關閉家裡的燈。
    與控制 widget 互動可能會在應用中開啟關聯的詳情檢視。這取決於控制 widget 的函式是否輸出任何資料,例如在搜尋 widget 的情況下。
  4. 混合微件
    雖然某些 widget 表示前幾部分中所述的型別之一(資訊、集合或控制),但許多 widget 都是將不同型別的元素組合在一起的混合 widget。例如,音樂播放器 widget 主要是一個控制 widget,但也會向使用者顯示當前正在播放的曲目,比如資訊 widget。

由於 widget 位於主螢幕上,因此它們必須與在主螢幕上建立的導航共存。與全屏應用相比,這限制了 widget 中提供的手勢支援。雖然應用可能會允許使用者在螢幕之間水平導航,但該手勢已在主螢幕上執行,用於在主螢幕之間進行導航。
只支援觸控和垂直滑動手勢。
鑑於可用於 widget 的手勢存在限制,某些依賴於受限手勢的介面構建塊不適用於 widget。

widget 是透過“宣傳”您的應用中提供的新鮮有趣內容來吸引使用者使用您的應用的好方法。

就像報紙頭版上的宣傳語一樣,widget 可整合和濃縮應用的資訊,並提供與應用中更豐富細節的連線。您可以說 widget 是資訊“零食”,而應用則是“餐飲”。確保應用顯示的資訊項詳細資訊比 widget 顯示的詳細資訊更多。

Jetpack Glance庫

[https://developer.android.google.cn/jetpack/androidx/releases/glance?hl=zh-cn]
[https://developer.android.google.cn/develop/ui/compose/glance/create-app-widget?hl=zh-cn]
Jetpack Glance 是在 Jetpack Compose 執行時的基礎上構建的框架,可讓您使用 Kotlin API 開發和設計應用 widget。應用 widget 是可以嵌入到其他應用中並接收定期更新的微型應用檢視。
Glance 提供了一組可組合項,可幫助您使用更少的程式碼快速構建適用於主螢幕的自適應 widget。本文件集中的頁面介紹瞭如何使用 Glance 構建應用 widget。
Glance 要求啟用 Compose,並且依賴於執行時、圖形和單元介面 Compose 層,但它無法與其他現有的 Jetpack Compose 介面元素直接互操作。請避免將兩者混淆。
Glance 提供了一種使用 Compose 構建應用 widget 的新方法,但受限於 AppWidgets 和 RemoteViews 的限制。因此,Glance 使用的可組合項與 Jetpack Compose 介面不同。

實現

核心程式碼

  1. build.gradle
compose_ui_version:String = '1.5.4'
dependencies {
    // compose相關
    implementation 'androidx.compose.material3:material3:1.2.1'
    implementation "androidx.compose.ui:ui:$compose_ui_version"
    implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version"
    implementation 'com.google.android.material:material:1.11.0'
    implementation platform('androidx.compose:compose-bom:2024.02.02')
    implementation 'androidx.compose.ui:ui-graphics'
    implementation 'androidx.compose.ui:ui-android:1.6.3'
    androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version"
    debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
    debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version"

    /* start Jetpack Glance小部件庫相關 */
    // For Glance support
    implementation "androidx.glance:glance:1.1.0-alpha01"
    // For AppWidgets support
    implementation "androidx.glance:glance-appwidget:1.1.0-alpha01"
    // For Wear-Tiles support
    implementation "androidx.glance:glance-wear-tiles:1.0.0-alpha05"
    /* end Jetpack Glance小部件庫相關 */
}

android {
    buildFeatures {
        compose true
    }

    composeOptions {
        kotlinCompilerExtensionVersion = "1.1.0-beta03"
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }
}
  1. 在清單中宣告 AppWidget
<!-- 對話方塊小部件 -->
<receiver android:name=".DialogWidgetReceiver"
    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/dialog_app_widget_info" />
</receiver>
  1. 從 GlanceAppWidgetReceiver 擴充套件 AppWidget 接收器
class DialogWidgetReceiver : GlanceAppWidgetReceiver() {
    override val glanceAppWidget: GlanceAppWidget = TODO("Create GlanceAppWidget")
}
  1. 新增 AppWidgetProviderInfo 後設資料
    dialog_app_widget_info.xml
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/glance_default_loading_layout"
    android:description="@string/i18n_dialog_widget_description"
    android:minWidth="100dp"
    android:minHeight="80dp"
    android:previewImage="@drawable/alittlesmile_widget_dialog"
    >

</appwidget-provider>
  1. 定義 GlanceAppWidget
    注意 :provideGlance 在主執行緒上執行。如需在 provideGlance 中執行任何長時間執行的操作,請使用 withContext 切換到另一個執行緒。如需詳細瞭解如何在主執行緒之外執行,請參閱使用協程確保主執行緒安全。
class MyAppWidget : GlanceAppWidget() {

    override suspend fun provideGlance(context: Context, id: GlanceId) {

        // In this method, load data needed to render the AppWidget.
        // Use `withContext` to switch to another thread for long running
        // operations.

        provideContent {
            // create your AppWidget here
            Text("Hello World")
        }
    }

    @Composable
    private fun MyContent() {
        Column(
            modifier = GlanceModifier.fillMaxSize(),
            verticalAlignment = Alignment.Top,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp))
            Row(horizontalAlignment = Alignment.CenterHorizontally) {
                Button(
                    text = "Home",
                    onClick = actionStartActivity<MainActivity>()
                )
                Button(
                    text = "Work",
                    onClick = actionStartActivity<MainActivity>()
                )
            }
        }
    }

}
  1. 在 GlanceAppWidgetReceiver 上的 glanceAppWidget 中對其進行例項化
class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {

    // Let MyAppWidgetReceiver know which GlanceAppWidget to use
    override val glanceAppWidget: GlanceAppWidget = MyAppWidget()
}

完整程式碼:
DialogWidget.kt

package cn.qsbye.alittlesmile_android

import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import androidx.glance.GlanceId
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
import androidx.glance.appwidget.provideContent

/* start 匯入Glance重名庫 */
import androidx.glance.Button
import androidx.glance.GlanceModifier
import androidx.glance.action.actionStartActivity
import androidx.glance.layout.Alignment
import androidx.glance.layout.Column
import androidx.glance.layout.Row
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.padding
import androidx.glance.text.Text
/* end 匯入Glance重名庫 */

// "學了嗎"對話方塊小部件接收器
class DialogWidgetReceiver: GlanceAppWidgetReceiver() {
    // 例項化小部件
    override val glanceAppWidget: GlanceAppWidget = DialogWidget()
}

class DialogWidget : GlanceAppWidget() {

    override suspend fun provideGlance(context: Context, id: GlanceId) {

        // In this method, load data needed to render the AppWidget.
        // Use `withContext` to switch to another thread for long running
        // operations.

        provideContent {
            // create your AppWidget here
            // Text("Hello World")
            DialogWidgetLayout()
        }
    }

    @Composable
    private fun DialogWidgetLayout() {
        Column(
            modifier = GlanceModifier.fillMaxSize(),
            verticalAlignment = Alignment.Top,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(text = "今天學習了嗎?", modifier = GlanceModifier.padding(12.dp))
            Row(horizontalAlignment = Alignment.CenterHorizontally) {
                Button(
                    text = "學了😄",
                    onClick = actionStartActivity<MainActivity>()
                )
                Button(
                    text = "沒學😠",
                    onClick = actionStartActivity<BootActivity>()
                )
            }
        }
    }
}
  1. 找到小部件並新增到桌面

效果

桌面新增小部件後 尋找小部件並新增

相關文章