使用 Glance 创建应用微件

使用 Glance 创建应用微件

1. 开始之前

本 Codelab 将带你为 SociaLite 创建应用微件。首先构建一个简单的 Glance 微件,并将它加入 SociaLite 和主屏幕;随后用 Glance 组件和主题添加未配置时的初始状态;接着支持用户从微件中选择最喜爱的联系人;最后学习如何从应用更新微件。

前提知识

  • 具备基础 Kotlin 知识。
  • 完成过设置 Android Studio练习,或熟悉 Android Studio,并能在 Android 15 模拟器或运行 Android 15 的实体设备上测试应用。
  • 具备基础 Hilt 知识。
  • 具备基础 Compose 知识。Glance 不使用 Jetpack Compose 的 UI composable,但复用了其框架和编码风格。

本练习将学习什么

  • 配置应用以支持微件。
  • 使用 Glance 组件构建响应式布局。
  • 使用 GlanceTheme,在用户的主屏幕上支持动态配色。
  • 处理微件中的用户交互。
  • 从应用更新微件。

所需条件

  • 最新版本的 Android Studio。
  • 运行 Android 12 或更高版本的测试设备或模拟器。
  • Android 12 或更高版本的 SDK。
主屏幕上的 SociaLite 微件
主屏幕上的 SociaLite 微件(Android Developers 原图)

2. 准备环境

获取起始代码

  1. 如果已经完成“处理 Android 15 的强制边到边显示”或“添加预测性返回动画”练习,你已经拥有起始代码,可以直接进入“添加微件”部分。
  2. 从 GitHub 下载起始代码;也可以克隆仓库并切换到 codelab_improve_android_experience_2024 分支:
 git clone git@github.com:android/socialite.git
 cd socialite
 git checkout codelab_improve_android_experience_2024

3. 在 Android Studio 中打开 SociaLite,并在 Android 15 设备或模拟器上运行。你会看到类似下面的界面:

采用手势导航的 SociaLite
采用手势导航的 SociaLite(Android Developers 原图)

采用手势导航的 SociaLite。

3. 添加微件

什么是微件

微件是应用中可嵌入其他 Android 应用的一部分,最常见的位置是用户主屏幕。添加微件可以让用户快速开始常见任务、查看一眼可读的信息,并通过你的内容个性化设备。

什么是 Glance

Jetpack Glance 是一个使用类似 Compose 的 Kotlin API 编写微件的库。它具有 Compose 的若干优点,例如重组、Kotlin 声明式界面代码和有明确设计规范的组件。Glance 大幅减少了微件中手写 XML RemoteViews 的需求。

创建微件

Android 微件在 AndroidManifest 中声明为 <receiver> 元素。此接收器应导出,处理 android.appwidget.action.APPWIDGET_UPDATE action 的 Intent,并通过名为 android.appwidget.provider 的 metadata 元素提供微件配置文件。

为 SociaLite 添加微件

主屏幕上的 SociaLite 微件
主屏幕上的 SociaLite 微件(Android Developers 原图)

我们希望用户通过微件看到最喜爱的联系人,以及是否有来自此人的未读消息。有未读消息时,点击微件应进入该联系人的聊天界面。同时,借助 Glance 组件与主题,使用响应式布局和动态配色改善外观。

先为 SociaLite 添加静态“Hello World”微件,再逐步扩展功能。需要完成五项工作:添加 Glance 依赖;实现 GlanceAppWidget;创建 GlanceAppWidgetReceiver;通过 app widget info XML 配置微件;将接收器和微件信息加入 AndroidManifest.xml。

向项目添加 Glance

起始代码已在版本目录 libs.versions.toml 中加入 Glance 版本与库坐标:

[versions]
//..

glance = "1.1.1"

[libraries]
glance-appwidget = { group = "androidx.glance", name = "glance-appwidget", version.ref = "glance" }
glance-material = { group = "androidx.glance", name = "glance-material3", version.ref = "glance" }
build.gradle.kts

SociaLite 的 app/build.gradle.kts 也已包含 Glance 依赖:

dependencies {
...
implementation(libs.glance.appwidget)
implementation(libs.glance.material)

...
}

如果修改了这些文件,请同步项目以下载 Glance 库。

创建 GlanceAppWidget 和 GlanceAppWidgetReceiver

Android 通过广播接收器通知 SociaLite:微件被添加、需要更新或已被移除。Glance 提供抽象接收器 GlanceAppWidgetReceiver,它继承 AppWidgetProvider,并负责提供 GlanceAppWidget 实例。后者把 Glance composable 渲染为 RemoteViews。

起始代码已包含两个类:继承 GlanceAppWidget 的 SociaLiteAppWidget,以及继承 GlanceAppWidgetReceiver 的 SociaLiteAppWidgetReceiver。

  1. 进入 app/src/main/java/com/google/android/samples/socialite/ 下的 widget 包。
  2. 打开 SociaLiteAppWidget,其中重写了 provideGlance。
  3. 把 TODO 替换为 provideContent 调用,并传入微件的 composable。暂时只显示“Hello World”,稍后再增加功能:
package com.google.android.samples.socialite.widget
import android.content.Context
import androidx.glance.GlanceId
import androidx.glance.GlanceTheme
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.provideContent
import androidx.glance.text.Text

class SociaLiteAppWidget : GlanceAppWidget() {
   override suspend fun provideGlance(context: Context, id: GlanceId) {
       provideContent {
           GlanceTheme {
               Text("Hello World")
           }
       }
   }
}

4. 打开 widget 包的 SociaLiteAppWidgetReceiver。目前只需提供微件实例,稍后再扩展功能。

5. 将 TODO 替换为 SociaLiteAppWidget() 构造调用:

package com.google.android.samples.socialite.widget

import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
class SociaLiteAppWidgetReceiver : GlanceAppWidgetReceiver() {
   override val glanceAppWidget: GlanceAppWidget = SociaLiteAppWidget()
}

现在可以配置 Android,让它显示微件并允许用户将微件添加到主屏幕。

添加 app-widget provider 信息

  1. 右键点击 res/xml,选择 New > XML resource file。
  2. 文件名输入 socialite_widget_info,根元素填 appwidget-provider,点击 OK。此文件包含 AppWidgetHost 初次显示微件所需的 metadata。
  3. 向 socialite_widget_info.xml 添加以下内容:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
   android:resizeMode="horizontal|vertical"
   android:updatePeriodMillis="3600000"
   android:minHeight="128dp"
   android:minWidth="128dp"

   android:minResizeHeight="128dp"
   android:minResizeWidth="128dp"
   android:configure="com.google.android.samples.socialite.widget.SociaLiteAppWidgetConfigActivity"
   android:widgetFeatures="configuration_optional|reconfigurable"
   android:previewImage="@drawable/widget_preview"
   android:maxResizeHeight="512dp"
   android:maxResizeWidth="512dp"
   android:targetCellWidth="2"
   android:targetCellHeight="2"
   android:initialLayout="@layout/glance_default_loading_layout">
</appwidget-provider>

这些属性的用途如下:

属性名 说明
resizeMode 允许水平和垂直调整微件大小。
targetCellWidth, targetCellHeight 指定首次加入主屏幕时的默认尺寸。
updatePeriodMillis 控制宿主何时可能决定刷新微件。应用只要正在运行且有新信息,就可以主动更新微件。
minResizeHeight,minResizeWidth 规定微件可以缩小到的最小尺寸。
minHeight,minWidth 规定加入主屏幕时的最小默认尺寸。
initialLayout 在 Glance 渲染 composable 期间显示初始布局。
previewImage 为微件选择器提供静态预览图。
widgetFeatures 向宿主提示微件支持的功能,标志本身不会改变微件行为。本例表示添加前不强制配置,而且添加后仍可重新配置。
configure 指定用于后续配置微件的 Activity 类名。

包括 API 31 及更高版本功能在内的完整属性列表,见 AppWidgetProviderInfo。

更新 AndroidManifest 并测试

最后更新 AndroidManifest.xml。在 application 内定义一个子元素 receiver,处理 APPWIDGET_UPDATE Intent,并向 Android Launcher 提供微件 metadata。

  1. 为 SociaLiteAppWidgetReceiver 创建导出的接收器,把下列内容放入 application 元素内部:
<receiver
   android:name=".widget.SociaLiteAppWidgetReceiver"
   android:exported="true"
   android:label="Favorite Contact">

   <intent-filter>
       <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
   </intent-filter>

   <meta-data
       android:name="android.appwidget.provider"
       android:resource="@xml/socialite_widget_info" />
</receiver>

2. 编译并运行应用。

3. 应用启动后,将微件加入主屏幕。例如在 Pixel 上,长按背景,选择 Widgets > SociaLite,然后添加微件。

透明背景的 Hello World 微件
透明背景的 Hello World 微件(Android Developers 原图)

目前微件显示“Hello World”,背景透明,外观和功能都很简单。下一节将加入更复杂的布局,并用 Material Design 配色改善外观。

4. 改善设计

当前静态微件仍缺少许多优秀微件应有的特性。好的微件应保持内容新鲜、简短,功能简单;用可调整大小的布局减少不自然的空隙;从微件宿主的背景中应用配色。深入讨论见微件设计指南。

添加 Scaffold

用 Glance Scaffold 作为微件的顶层组件。它提供简单的插槽 API,用于显示带 TitleBar 的微件界面,并将背景色设为 GlanceTheme.colors.widgetBackground、应用内边距。

1. 用以下代码替换 SociaLiteAppWidget 的实现:

package com.google.android.samples.socialite.widget

import android.content.Context
import androidx.compose.runtime.Composable
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.ImageProvider
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.components.Scaffold
import androidx.glance.appwidget.components.TitleBar
import androidx.glance.appwidget.provideContent
import androidx.glance.layout.fillMaxSize
import androidx.glance.text.Text
import com.google.android.samples.socialite.R

class SociaLiteAppWidget : GlanceAppWidget() {
   override suspend fun provideGlance(context: Context, id: GlanceId) {
       provideContent {
           GlanceTheme() {
               Content()
           }
       }
   }
   @Composable
   private fun Content() {
       Scaffold(titleBar = {TitleBar(startIcon = ImageProvider(R.drawable.ic_launcher_monochrome), title = "SociaLite")},
           modifier = GlanceModifier.fillMaxSize()) {
           Text("Hello World")
       }
   }
}

2. 重新运行应用,并向主屏幕添加一个新的微件实例,以查看更新。

带背景和标题的微件
带背景和标题的微件(Android Developers 原图)

记住,微件是显示在外部宿主中的 RemoteViews。后面会加入从应用自动更新微件的功能;在那之前,每次修改代码都需要通过微件选择器重新添加实例,才能看到变化。

现在的外观已有改善,但与其他微件相比,颜色似乎仍不协调。主屏幕微件通常应跟随用户主题设置。通过动态颜色 token,可以让微件主题适应设备壁纸和主题。

添加动态配色

将 Scaffold 背景设置为 widgetBackground,将 TitleBar 文本及 Text 的颜色设置为 onSurface。修改文字样式需要导入 Glance 的 TextStyle;修改 Scaffold 背景则设置其 backgroundColor 属性。

1. 在 SociaLiteAppWidget.kt 中增加导入:

//Add to the imports section of your Kotlin code.
import androidx.glance.text.TextStyle

2. 更新 Content composable:

Scaffold(
   titleBar = {
       TitleBar(
           textColor = GlanceTheme.colors.onSurface,
           startIcon = ImageProvider(R.drawable.ic_launcher_monochrome),
           title = "SociaLite",
       )
   },
   backgroundColor = GlanceTheme.colors.widgetBackground,
   modifier = GlanceModifier.fillMaxSize(),
) {
   Text(text = "Hello World", style = TextStyle(color = GlanceTheme.colors.onSurface))
}

3. 重新运行应用,并向主屏幕添加新的微件实例。

微件现在会与主屏幕的其他微件保持主题协调,并在更换壁纸或切换深色模式时自动更新颜色。对于色彩丰富的壁纸,微件还会适应其所在位置的背景。

浅色主题的微件
浅色主题的微件(Android Developers 原图)
深色主题的微件
深色主题的微件(Android Developers 原图)

添加未配置时的初始状态

接下来考虑微件的状态和配置。微件加入主屏幕后若仍需配置,通常最好先显示零状态(zero state),提示用户完成配置。本例将添加一个配置 Activity,并从零状态跳转到它。

起始代码提供了存储、访问和修改微件配置状态的类。你将添加代码,让微件界面显示这些状态,并通过 lambda action 处理点击。

查看微件模型

查看 com.google.android.samples.socialite.widget.model 包中的 WidgetModel、WidgetModelDao 和 WidgetModelRepository。这些类已包含在起始代码中,负责把微件状态持久化到 Room 数据库,并使用 Hilt 管理生命周期。

WidgetModel 包含 Android 分配的 widgetId、显示的 SociaLite 联系人的 contactId、用于显示的 displayName 与 photo,以及表示联系人是否有未读消息的布尔值。SociaLiteAppWidget 的 composable 会读取这些数据。

WidgetModelDao 是封装数据库访问的数据访问对象。WidgetModelRepository 提供创建、读取、更新、删除 WidgetModel 实例的便捷函数。这些类由 Hilt 创建,通过依赖注入进入应用。

打开 app/src/main/java/com/google/android/samples/socialite/widget/model/WidgetModel.kt。这是一个带 Entity 注解的 data 类。Android 为每个微件实例分配独立 ID,SociaLite 将它用作模型数据主键;每个模型记录对应联系人的基本信息和未读消息状态:

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = Contact::class,
            parentColumns = ["id"],
            childColumns = ["contactId"],
            onDelete = ForeignKey.CASCADE,
        ),
    ],
    indices = [
        Index("widgetId"),
        Index("contactId"),
    ],
)
data class WidgetModel(
    @PrimaryKey val widgetId: Int,
    val contactId: Long,
    val displayName: String,
    val photo: String,
    val unreadMessages: Boolean = false,
) : WidgetState

零状态

让 Content 从 WidgetModelRepository 加载模型。若无模型,则显示 ZeroState;否则显示正常内容。目前正常内容暂时仍是“Hello World”,下一部分再改进。使用 when 表达式选择 ZeroState 或 Text 占位内容。

1. 在 provideGlance 中、composable 外部获取仓库和当前微件 ID,在 provideContent 之前加入:

override suspend fun provideGlance(context: Context, id: GlanceId) {
   val widgetId = GlanceAppWidgetManager(context).getAppWidgetId(id)
   val repository = WidgetModelRepository.get(context)

可能还需要这些导入:

import com.google.android.samples.socialite.widget.model.WidgetModel
import com.google.android.samples.socialite.widget.model.WidgetModelRepository
import com.google.android.samples.socialite.widget.model.WidgetState.Loading
import androidx.glance.appwidget.GlanceAppWidgetManager

2. 给 Content 增加仓库和微件 ID 参数,用它们加载模型;更新函数签名并加入:

private fun Content(repository: WidgetModelRepository, widgetId: Int) {
   val model = repository.loadModel(widgetId).collectAsState(Loading).value

3. 如果 Android Studio 没有自动添加导入,请手动补上:

import androidx.compose.runtime.collectAsState

同时更新 provideGlance,把 ID 和仓库传给 Content:

override suspend fun provideGlance(context: Context, id: GlanceId) {
        val widgetId = GlanceAppWidgetManager(context).getAppWidgetId(id)
        val repository = WidgetModelRepository.get(context)

        provideContent {
            GlanceTheme {
                Content(repository, widgetId)
            }
        }
    }

4. 根据模型是否存在决定显示状态。将 Scaffold 和微件内容移入 ZeroState,把原来的 Scaffold 及其内容替换为:

   when (model) {
       is WidgetModel -> {Text("Hello World")}
       else -> ZeroState(widgetId)
   }

起始代码已在 com.google.android.samples.socialite.widget.ui 包中提供 ZeroState。

5. 如果 IDE 未自动导入,在 SociaLiteAppWidget 中加入:

import com.google.android.samples.socialite.widget.ui.ZeroState

6. 重新运行应用并添加新的微件实例。你会看到零状态组件和一个按钮;点击按钮会打开配置 Activity。下一节将从该 Activity 更新微件状态。

显示选择联系人按钮的零状态
显示选择联系人按钮的零状态(Android Developers 原图)
可选择四个联系人的配置界面
可选择四个联系人的配置界面(Android Developers 原图)

配置 Activity

查看 com.google.android.samples.socialite.widget.ui 包中的 ZeroState.kt:

@Composable
fun ZeroState(widgetId: Int) {
   val widgetIdKey = ActionParameters.Key<Int>(AppWidgetManager.EXTRA_APPWIDGET_ID)
Scaffold(
   titleBar = {
       TitleBar(
           modifier = GlanceModifier.clickable(actionStartActivity(MainActivity::class.java)),
           textColor = GlanceTheme.colors.onSurface,
           startIcon = ImageProvider(R.drawable.ic_launcher_monochrome),
           title = "SociaLite",
       )
   },
   backgroundColor = GlanceTheme.colors.widgetBackground,
   modifier = GlanceModifier.fillMaxSize(),
) {
   Box(modifier = GlanceModifier.fillMaxSize(), contentAlignment = Alignment.Center) {
       Button(
           text = "Select Favorite Contact",
           onClick = actionStartActivity<SociaLiteAppWidgetConfigActivity>(
               parameters = actionParametersOf(widgetIdKey to widgetId),
           ),
       )
   }
}

}

Scaffold 已移入 ZeroState。TitleBar 的 clickable 修饰符打开 SociaLite 主 Activity。Glance Button 提示用户采取行动,点击后打开 SociaLiteAppWidgetConfigActivity,并把微件 ID 作为 Intent extra 传入。这两个操作都使用 Glance 的 actionStartActivity 便捷函数。详见处理用户交互。

1. 查看 SociaLiteAppWidgetConfigActivity 如何更新配置。它是微件的配置 Activity,从 Intent 中读取键为 AppWidgetManager.EXTRA_APPWIDGET_ID 的整数 extra。更多说明见允许用户配置应用微件。

2. 在该 Activity 中,把 ContactRow 的 onClick 属性里的 TODO 替换为:

{
  coroutineScope.launch {

     widgetModelRepository.createOrUpdate(
       WidgetModel(
           appWidgetId,
           contact.id,
           contact.name,
           contact.iconUri.toString(),
           false,
       ),
     )
     SociaLiteAppWidget().updateAll(this@SociaLiteAppWidgetConfigActivity)
     val resultValue = Intent().putExtra(
       AppWidgetManager.EXTRA_APPWIDGET_ID,
       appWidgetId,
     )
     setResult(RESULT_OK, resultValue)
     finish()
  }
}

如果 Android Studio 未自动导入,补上:

import com.google.android.samples.socialite.widget.model.WidgetModel
import androidx.glance.appwidget.updateAll
import kotlinx.coroutines.launch

代码先通过仓库保存或更新包含所选联系人信息的 WidgetModel,再调用挂起函数 updateAll 更新主屏幕上的所有微件。应用的任何位置都可以调用该函数。最后设置配置 Activity 的返回结果,表示更新成功,并结束 Activity。

3. 运行应用并重新放置微件,查看新的零状态:

显示选择联系人按钮的零状态
显示选择联系人按钮的零状态(Android Developers 原图)

4. 点击 Select favorite contact,进入配置界面:

联系人配置界面
联系人配置界面(Android Developers 原图)
配置后显示 Hello World 的微件
配置后显示 Hello World 的微件(Android Developers 原图)

5. 选择联系人后,微件会更新,但还不会显示联系人;下一部分会添加此功能。

管理微件数据

  1. 打开 App inspection 工具,必要时连接到进程,选择 Database inspector 标签页,查看应用数据库。
  2. 在微件中选择最喜爱的联系人,确认微件显示“Hello World”。回到 App inspection,Widget model 表中应有该微件的一条记录。可能需要刷新表,或启用 Live updates 才能看到变化。
Database Inspector 中的微件模型记录
Database Inspector 中的微件模型记录(Android Developers 原图)

3. 再添加一个微件并选择另一联系人;同样可能需要刷新表或启用实时更新。

4. 移除微件,观察数据库中仍残留模型记录。

可以在 SociaLiteAppWidgetReceiver 中重写 onDeleted 清理记录。调用 WidgetModelRepository.cleanupWidgetModels 即可清理孤立模型;仓库由 Hilt 管理,因此通过依赖注入获取实例。

5. 给接收器类加上 Hilt 的 AndroidEntryPoint 注解,并注入 WidgetModelRepository。

6. 在重写的 onDeleted 方法中调用清理函数:

package com.google.android.samples.socialite.widget

import android.content.Context
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
import com.google.android.samples.socialite.widget.model.WidgetModelRepository
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

@AndroidEntryPoint
class SociaLiteAppWidgetReceiver : GlanceAppWidgetReceiver() {
   override val glanceAppWidget: GlanceAppWidget = SociaLiteAppWidget()

   @Inject
   lateinit var repository: WidgetModelRepository

   override fun onDeleted(context: Context, appWidgetIds: IntArray) {
       super.onDeleted(context, appWidgetIds)
       repository.cleanupWidgetModels(context)
   }

}

7. 重新运行应用。移除主屏幕微件时,App inspector 中相应的模型行现在也应消失。

5. 添加联系人界面,并在收到新消息时更新

最后实现联系人界面,并在联系人有未读消息时更新微件。

1. 查看 model 包中的 WidgetModelRepository,其中的 updateUnreadMessagesForContact 根据联系人 ID 更新相关微件。以下代码已存在,无需再次添加:

//Don't add this code.
fun updateUnreadMessagesForContact(contactId: Long, unread: Boolean) {
   coroutineScope.launch {
       widgetModelDao.modelsForContact(contactId).filterNotNull().forEach { model ->
           widgetModelDao.update(
             WidgetModel(model.widgetId, model.contactId, model.displayName, model.photo, unread)
           )
           SociaLiteAppWidget().updateAll(appContext)
       }
   }
}

方法接收联系人 ID contactId 与表示未读状态的布尔值 unread。它通过 WidgetModelDao 找到所有显示该联系人的模型,更新已读状态,再调用 Glance 的 SociaLiteAppWidget().updateAll 更新用户主屏幕上的微件。

了解状态更新机制后,创建联系人界面、发送消息并观察更新。把 FavoriteContact composable 加入 SociaLiteAppWidget 布局,并根据状态决定显示没有新消息还是有新消息。

2. 查看 com.google.android.samples.socialite.widget.ui 中的 FavoriteContact.kt。以下代码已存在,无需添加:

//Don't add this code.
@Composable
fun FavoriteContact(model: WidgetModel, onClick: Action) {
   Column(
       modifier = GlanceModifier.fillMaxSize().clickable(onClick)
           .background(GlanceTheme.colors.widgetBackground).appWidgetBackground()
           .padding(bottom = 8.dp),
       verticalAlignment = Alignment.Vertical.Bottom,
       horizontalAlignment = Alignment.Horizontal.CenterHorizontally,
   ) {
       Image(
           modifier = GlanceModifier.fillMaxWidth().wrapContentHeight().defaultWeight()
               .cornerRadius(16.dp),
           provider = ImageProvider(model.photo.toUri()),
           contentScale = ContentScale.Crop,
           contentDescription = model.displayName,
       )
       Column(
           modifier = GlanceModifier.fillMaxWidth().wrapContentHeight().padding(top = 4.dp),
           verticalAlignment = Alignment.Vertical.Bottom,
           horizontalAlignment = Alignment.Horizontal.CenterHorizontally,
       ) {
           Text(
               text = model.displayName,
               style = TextStyle(
                   fontWeight = FontWeight.Bold,
                   fontSize = 24.sp,
                   color = (GlanceTheme.colors.onSurface),
               ),
           )

           Text(
               text = if (model.unreadMessages) "New Message!" else "No messages",
               style = TextStyle(
                   fontWeight = FontWeight.Bold,
                   fontSize = 16.sp,
                   color = (GlanceTheme.colors.onSurface),
               ),
           )
       }
   }
}

3. 在 SociaLiteAppWidget 的 Content 中,用 FavoriteContact 替换 Text("Hello World")。它接收 WidgetModel 和通过 Glance actionStartActivity 创建的 Action。

4. 在 when 中为 WidgetModel 分支加入调用,其他情况继续显示 ZeroState:

when (model) {
  is WidgetModel -> FavoriteContact(model = model, onClick = actionStartActivity(
     Intent(LocalContext.current.applicationContext, MainActivity::class.java)
         .setAction(Intent.ACTION_VIEW)
         .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
         .setData("https://socialite.google.com/chat/${model.contactId}".toUri()))
  )
  else -> ZeroState(widgetId)
}

5. 如果 IDE 未自动导入,补上:

import com.google.android.samples.socialite.widget.ui.FavoriteContact
import androidx.glance.appwidget.action.actionStartActivity
import android.content.Intent
import com.google.android.samples.socialite.MainActivity
import androidx.core.net.toUri

6. 运行应用。

7. 选择最喜爱的联系人,发送一条消息,并在对方回复前立即退出应用。回复到达时,微件状态应改变。

8. 点击微件打开聊天,回到主屏幕时观察状态再次更新。

浅色主题下完成的联系人微件
浅色主题下完成的联系人微件(Android Developers 原图)

6. 完成

完成本练习后,你已经学习了如何使用 Glance 编写微件:适应不同主屏幕的外观,处理用户输入,并主动更新内容。

要获取 main 分支中的解决方案代码,如果已下载 SociaLite,执行:

git checkout main

否则重新克隆仓库,查看默认的 main 分支:

git clone git@github.com:android/socialite.git

了解更多

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容