Android开发笔记[17]-桌面小部件

摘要

使用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. 找到小部件并添加到桌面

效果

桌面添加小部件后 寻找小部件并添加
posted @ 2024-03-27 00:33  qsBye  阅读(168)  评论(0编辑  收藏  举报