wear-widgets
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWear OS widgets use ( and
) with (Remote Compose).
They serialize declarative UI and state documents to the watch host process.
androidx.glance.wearGlanceWearWidgetGlanceWearWidgetServiceandroidx.compose.remoteWear OS 小组件使用 ( 和 )与 (Remote Compose)。
它们会将声明式 UI 和状态文档序列化后发送到手表宿主进程。
androidx.glance.wearGlanceWearWidgetGlanceWearWidgetServiceandroidx.compose.remotePrerequisites and setup
前提条件与设置
-
Resolve library versions : Resolve the latest versions for(
{GLANCE_WEAR_VERSION}),androidx.glance.wear({COMPOSE_REMOTE_VERSION}), andandroidx.compose.remote({WEAR_COMPOSE_REMOTE_VERSION}). Includeandroidx.wear.compose.remote,-alpha, or-betareleases when no stable version exists yet. Don't run-rcto resolve versions:./gradlew dependencies- Use an available dependency lookup tool if present.
- Otherwise, fetch the latest version from Google Maven metadata (Glance Wear metadata, Compose Remote metadata, Wear Compose Remote metadata).
-
SDK and Compose compiler requirements : Ask permission before modifying repo-wide Gradle files unless the user requested widget creation, tile migration, or dependency setup. Setor higher and enable
compileSdk = 37. Keep the project's existing Android and Kotlin Gradle plugin declarations intact.buildFeatures { compose = true }dependencies { implementation("androidx.glance.wear:wear:{GLANCE_WEAR_VERSION}") implementation("androidx.glance.wear:wear-core:{GLANCE_WEAR_VERSION}") implementation("androidx.glance.wear:wear-tooling-preview:{GLANCE_WEAR_VERSION}") implementation("androidx.compose.remote:remote-creation-compose:{COMPOSE_REMOTE_VERSION}") implementation("androidx.compose.remote:remote-creation:{COMPOSE_REMOTE_VERSION}") implementation("androidx.compose.remote:remote-core:{COMPOSE_REMOTE_VERSION}") implementation("androidx.compose.remote:remote-tooling-preview:{COMPOSE_REMOTE_VERSION}") implementation("androidx.wear.compose.remote:remote-material3:{WEAR_COMPOSE_REMOTE_VERSION}") }
-
解析库版本:解析(
{GLANCE_WEAR_VERSION})、androidx.glance.wear({COMPOSE_REMOTE_VERSION})和androidx.compose.remote({WEAR_COMPOSE_REMOTE_VERSION})的最新版本。如果尚无稳定版本,请包含androidx.wear.compose.remote、-alpha或-beta版本。不要运行-rc来解析版本:./gradlew dependencies- 如果存在可用的依赖项查询工具,请使用该工具。
- 否则,从 Google Maven 元数据获取最新版本(Glance Wear metadata、Compose Remote metadata、Wear Compose Remote metadata)。
-
SDK 与 Compose compiler 要求:除非用户已要求创建小组件、迁移磁贴或配置依赖项,否则在修改仓库级 Gradle 文件前必须征得许可。将设置为
compileSdk或更高版本,并启用37。保持项目中现有的 Android 和 Kotlin Gradle plugin 声明不变。buildFeatures { compose = true }dependencies { implementation("androidx.glance.wear:wear:{GLANCE_WEAR_VERSION}") implementation("androidx.glance.wear:wear-core:{GLANCE_WEAR_VERSION}") implementation("androidx.glance.wear:wear-tooling-preview:{GLANCE_WEAR_VERSION}") implementation("androidx.compose.remote:remote-creation-compose:{COMPOSE_REMOTE_VERSION}") implementation("androidx.compose.remote:remote-creation:{COMPOSE_REMOTE_VERSION}") implementation("androidx.compose.remote:remote-core:{COMPOSE_REMOTE_VERSION}") implementation("androidx.compose.remote:remote-tooling-preview:{COMPOSE_REMOTE_VERSION}") implementation("androidx.wear.compose.remote:remote-material3:{WEAR_COMPOSE_REMOTE_VERSION}") }
Codebase exploration
代码库探索
- Search the module for existing ,
TileService, orMaterial3TileServiceimplementations,GlanceWearWidgetentries in<service>, andapp/src/main/AndroidManifest.xmlpreview assets under@drawable/.app/src/main/res/ - When migrating an existing tile or modifying an unfamiliar multi-module project, run to verify baseline build health.
./gradlew :app:assembleDebug
- 在模块中搜索现有的 、
TileService或Material3TileService实现、GlanceWearWidget中的app/src/main/AndroidManifest.xml条目,以及<service>下app/src/main/res/目录中的预览资源。@drawable/ - 迁移现有磁贴或修改不熟悉的多模块项目时,运行 ,验证基线构建状态。
./gradlew :app:assembleDebug
Workflow
工作流
Step 1: Configure AndroidManifest.xml and provider XML
第 1 步:配置 AndroidManifest.xml 和 provider XML
Register the subclass in
with the permission and
intent action. Attach pointing to an
resource with and
entries. When configuring the service, provider XML, or legacy
migration, you MUST follow Widget service and manifest reference.
GlanceWearWidgetServiceapp/src/main/AndroidManifest.xmlBIND_TILE_PROVIDERBIND_WIDGET_PROVIDER<meta-data>@xml/<wearwidget-provider>SMALLLARGE<container>TileServicexml
<service
android:name=".snippets.widget.skill.MyWidgetService"
android:exported="true"
android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">
<intent-filter>
<action android:name="androidx.glance.wear.action.BIND_WIDGET_PROVIDER" />
</intent-filter>
<meta-data
android:name="androidx.glance.wear.widget.provider"
android:resource="@xml/my_widget_info" />
</service>在 中注册 子类,并为其配置 权限和 intent action。附加 ,使其指向包含 和 条目的 资源。配置 service、provider XML 或迁移旧版 时,必须遵循小组件 service 与 manifest 参考文档。
app/src/main/AndroidManifest.xmlGlanceWearWidgetServiceBIND_TILE_PROVIDERBIND_WIDGET_PROVIDER<meta-data>SMALLLARGE<container>@xml/<wearwidget-provider>TileServicexml
<service
android:name=".snippets.widget.skill.MyWidgetService"
android:exported="true"
android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">
<intent-filter>
<action android:name="androidx.glance.wear.action.BIND_WIDGET_PROVIDER" />
</intent-filter>
<meta-data
android:name="androidx.glance.wear.widget.provider"
android:resource="@xml/my_widget_info" />
</service>Step 2: Implement GlanceWearWidget and GlanceWearWidgetService
第 2 步:实现 GlanceWearWidget 和 GlanceWearWidgetService
Extend and override returning
. Annotate the subclass with
for static provider resolution:
GlanceWearWidgetprovideWidgetDataWearWidgetDocumentGlanceWearWidgetService@AssociateWithGlanceWearWidgetkotlin
// WRONG: Mobile GlanceAppWidget API (don't use on Wear OS)
class BrokenWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {}
}
// CORRECT: Wear OS GlanceWearWidget + GlanceWearWidgetService
@AssociateWithGlanceWearWidget(MyWidget::class)
class MyWidgetService : GlanceWearWidgetService() {
override val widget: GlanceWearWidget = MyWidget()
}
class MyWidget : GlanceWearWidget() {
override suspend fun provideWidgetData(
context: Context,
params: WearWidgetParams,
): WearWidgetData =
WearWidgetDocument(
background = WearWidgetBrush.color(Color(0xFF1C1B1F).rc)
) {
MyWidgetContent(params = params)
}
}继承 并重写 ,返回 。为 子类添加 注解,以进行静态 provider 解析:
GlanceWearWidgetprovideWidgetDataWearWidgetDocumentGlanceWearWidgetService@AssociateWithGlanceWearWidgetkotlin
// WRONG: Mobile GlanceAppWidget API (don't use on Wear OS)
class BrokenWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {}
}
// CORRECT: Wear OS GlanceWearWidget + GlanceWearWidgetService
@AssociateWithGlanceWearWidget(MyWidget::class)
class MyWidgetService : GlanceWearWidgetService() {
override val widget: GlanceWearWidget = MyWidget()
}
class MyWidget : GlanceWearWidget() {
override suspend fun provideWidgetData(
context: Context,
params: WearWidgetParams,
): WearWidgetData =
WearWidgetDocument(
background = WearWidgetBrush.color(Color(0xFF1C1B1F).rc)
) {
MyWidgetContent(params = params)
}
}Step 3: Build Remote Compose layouts, state, and actions
第 3 步:构建 Remote Compose 布局、状态和操作
Remote Compose evaluates UI and state inside the Wear OS host process rather
than the app process. When building adaptive layouts or full-bleed backgrounds,
you MUST follow Remote Compose layouts reference. When implementing state
or click actions, you MUST follow State and actions reference.
- Remote value wrappers : Convert primitives using (
.rs),RemoteString(.rc),RemoteColor(.rdp), andRemoteDp(.ri). Format integers with theRemoteIntmember method.RemoteInt.toRemoteString - Adaptive container sizing : Branch layout density, typography, and item counts on (
params.containerTypeversusContainerInfo.CONTAINER_TYPE_SMALL).ContainerInfo.CONTAINER_TYPE_LARGE - Unmasked full-bleed backgrounds : Render background bitmaps inside a root (
RemoteBox) usingRemoteModifier.fillMaxSizeandRemoteImage(RemoteImageBitmap). Keep background artwork unmasked (bitmap.asImageBitmap) withoutcornerRadius = 0dporCircleShapeclipping. The host clips edges to the watch shape.RoundedCornerShape - Declarative host state and PendingIntent actions : Mutate local widget state with and
rememberMutableRemoteIntinstead of in-memory Compose state (valueChange(state, newValue)ormutableStateOf). Trigger activity launches or background broadcasts usingmutableIntStateOf(always passing apendingIntentAction { ctx -> ... }lambda). Refresh active widgets with(Context) -> PendingIntent.widget.triggerUpdateAll(context)
Remote Compose 会在 Wear OS 宿主进程而非应用进程中计算 UI 和状态。构建自适应布局或全幅背景时,必须遵循Remote Compose 布局参考文档。实现状态或点击操作时,必须遵循状态与操作参考文档。
- Remote 值包装器:使用 (
.rs)、RemoteString(.rc)、RemoteColor(.rdp)和RemoteDp(.ri)转换基本类型。使用RemoteInt成员方法格式化整数。RemoteInt.toRemoteString - 自适应容器尺寸:根据 (
params.containerType与ContainerInfo.CONTAINER_TYPE_SMALL)对布局密度、排版和项目数量进行分支处理。ContainerInfo.CONTAINER_TYPE_LARGE - 未遮罩的全幅背景:在根 (使用
RemoteBox)中通过RemoteModifier.fillMaxSize和RemoteImage(RemoteImageBitmap)渲染背景位图。保持背景图未遮罩(bitmap.asImageBitmap),不要使用cornerRadius = 0dp或CircleShape进行裁剪。宿主会将边缘裁剪为手表形状。RoundedCornerShape - 声明式宿主状态与 PendingIntent 操作:使用 和
rememberMutableRemoteInt修改本地小组件状态,而不是使用内存中的 Compose 状态(valueChange(state, newValue)或mutableStateOf)。使用mutableIntStateOf触发 activity 启动或后台广播,并始终传入pendingIntentAction { ctx -> ... }lambda。使用(Context) -> PendingIntent刷新活动中的小组件。widget.triggerUpdateAll(context)
Step 4: Add WearWidgetPreview functions and picker preview PNGs
第 4 步:添加 WearWidgetPreview 函数和选择器预览 PNG
- Composable border previews () : Import
androidx.glance.wear.tooling.preview,WearWidgetPreview,SquircleAllWidgetPreviewParams, andRoundAllWidgetPreviewParamsfromRectangularAllWidgetPreviewParams(NEVERandroidx.glance.wear.tooling.preview). Defineandroidx.glance.wear.testingfunctions invoking@Preview.WearWidgetPreview(widget, params) - Rectangular picker PNGs () : Use
res/drawable-nodpi/atRectangularAllWidgetPreviewParamsor render non-blank, unmasked rectangular PNGs for every320 dpiin the provider XML:<container>- (
SMALL):CONTAINER_TYPE_SMALLpx (448x168dp at 320 dpi, aspect ratio224x84).2.67 - (
LARGE):CONTAINER_TYPE_LARGEpx (464x288dp at 320 dpi, aspect ratio232x144).1.61 - Each preview PNG MUST depict the widget's actual UI elements, including backgrounds, text labels, icons, avatar initials, and buttons. NEVER use a blank placeholder, empty unlabeled shapes, or a 1:1 tile preview. When adding composable previews, picker PNGs, or unit tests, you MUST follow Previews and testing reference.
- Composable 边框预览():从
androidx.glance.wear.tooling.preview导入androidx.glance.wear.tooling.preview、WearWidgetPreview、SquircleAllWidgetPreviewParams和RoundAllWidgetPreviewParams(绝不使用RectangularAllWidgetPreviewParams)。定义调用androidx.glance.wear.testing的WearWidgetPreview(widget, params)函数。@Preview - 矩形选择器 PNG():使用
res/drawable-nodpi/和RectangularAllWidgetPreviewParams,或为 provider XML 中的每个320 dpi渲染非空白、未遮罩的矩形 PNG:<container>- (
SMALL):CONTAINER_TYPE_SMALL像素(在 320 dpi 下为448x168dp,宽高比为224x84)。2.67 - (
LARGE):CONTAINER_TYPE_LARGE像素(在 320 dpi 下为464x288dp,宽高比为232x144)。1.61 - 每个预览 PNG 必须呈现小组件的实际 UI 元素,包括背景、文字标签、图标、头像首字母和按钮。绝不使用空白占位图、无标签空形状或 1:1 磁贴预览。添加 Composable 预览、选择器 PNG 或单元测试时,必须遵循预览与测试参考文档。
Component mapping
组件映射
Use this sample table to load complete implementation patterns and imports:
| Capability or API surface | Reference guide |
|---|---|
| Widget service and manifest reference |
| Remote Compose layouts reference |
| State and actions reference |
| Previews and testing reference |
参考下表获取完整的实现模式和导入项:
| 功能或 API 接口 | 参考指南 |
|---|---|
| 小组件 service 与 manifest 参考文档 |
| Remote Compose 布局参考文档 |
| 状态与操作参考文档 |
| 预览与测试参考文档 |
Legacy ProtoLayout and mobile Glance equivalents
旧版 ProtoLayout 和移动端 Glance 等价项
| Legacy or mobile API | Wear OS widget API | Action to take |
|---|---|---|
| | Replace service and annotate with |
| | Return |
| | Build layouts with |
| | Mutate remote state declaratively using |
| | Pass a |
| 旧版或移动端 API | Wear OS 小组件 API | 执行操作 |
|---|---|---|
| | 替换 service,并添加 |
| | 从 |
| | 使用 |
| | 使用 |
| | 向 |
Troubleshooting
故障排查
| Symptom | Cause | Fix |
|---|---|---|
Unresolved reference | Missing | Set |
Unresolved reference | Imported | Remove the import; call |
Type mismatch on | Passed bare | Wrap in a lambda: |
Unresolved | Other classes or source sets in the module still use legacy tile APIs | Migrate or update the remaining references before removing legacy |
| 症状 | 原因 | 修复方式 |
|---|---|---|
无法解析 | 缺少 | 将 |
无法解析 | 将 | 移除该导入;将其作为成员方法调用: |
| 传入了裸 | 使用 lambda 包装: |
迁移后仍无法解析 | 模块中的其他类或 source set 仍在使用旧版磁贴 API | 在从 |
Verification
验证
- Run to verify that the widget, service,
./gradlew :app:assembleDebugdeclarations, andAndroidManifest.xml@xml/resources compile and link without errors.<wearwidget-provider> - If the project includes unit tests or new widget tests, run and confirm all tests pass. When adding composable previews, picker PNGs, or unit tests, you MUST follow Previews and testing reference.
./gradlew :app:testDebugUnitTest
- 运行 ,验证小组件、service、
./gradlew :app:assembleDebug声明以及AndroidManifest.xml@xml/资源能够成功编译和链接,且没有错误。<wearwidget-provider> - 如果项目包含单元测试或新增了小组件测试,运行 并确认所有测试均通过。添加 Composable 预览、选择器 PNG 或单元测试时,必须遵循预览与测试参考文档。
./gradlew :app:testDebugUnitTest
Antipatterns
反模式
- NEVER extend mobile or
GlanceAppWidget, or use deprecatedAppWidgetProviderorGlanceTileServicefor Wear OS widgets.TileService - NEVER use in-memory client Compose state (,
mutableStateOf) or in-process click lambdas for Remote Compose UI.mutableIntStateOf - NEVER pass a bare instance to
PendingIntent; always pass apendingIntentActionlambda.(Context) -> PendingIntent - NEVER apply or
CircleShapeclipping to full-bleed widget background artwork. NEVER reuse 1:1 (RoundedCornerShape) tile previews or blank solid-color placeholders for widget picker PNGs.400x400
- 绝不继承移动端 或
GlanceAppWidget,也不要在 Wear OS 小组件中使用已弃用的AppWidgetProvider或GlanceTileService。TileService - 绝不在 Remote Compose UI 中使用内存中的客户端 Compose 状态(、
mutableStateOf)或进程内点击 lambda。mutableIntStateOf - 绝不向 传入裸
pendingIntentAction实例;始终传入PendingIntentlambda。(Context) -> PendingIntent - 绝不对全幅小组件背景图应用 或
CircleShape裁剪。绝不将 1:1(RoundedCornerShape)磁贴预览或纯色空白占位图用作小组件选择器 PNG。400x400
Best practices
最佳实践
- MUST declare and
com.google.android.wearable.permission.BIND_TILE_PROVIDERonandroidx.glance.wear.action.BIND_WIDGET_PROVIDER.<service> - MUST declare and
SMALLLARGEentries in<container>with domain-accurate@xml/and448x168PNGs in464x288.res/drawable-nodpi/ - MUST adapt layout content to and include
params.containerTypefunctions for both@PreviewandSquircleAllWidgetPreviewParams.RoundAllWidgetPreviewParams
- 必须在 上声明
<service>和com.google.android.wearable.permission.BIND_TILE_PROVIDER。androidx.glance.wear.action.BIND_WIDGET_PROVIDER - 必须在 中声明
@xml/和SMALLLARGE条目,并在<container>中提供与实际尺寸匹配的res/drawable-nodpi/和448x168PNG。464x288 - 必须根据 调整布局内容,并为
params.containerType和SquircleAllWidgetPreviewParams包含RoundAllWidgetPreviewParams函数。@Preview