开发环境
- Android Studio Iguana+。
- Wear OS SDK、Emulator。
compose-material3+compose-material3-windowsizeclass。
Gradle 配置:
dependencies {
implementation("androidx.wear.compose:compose-foundation:1.3.0")
implementation("androidx.wear.compose:compose-material:1.3.0")
implementation("androidx.wear.tiles:tiles:1.3.0")
implementation("androidx.wear.tiles:tiles-material:1.3.0")
}
Compose 布局差异
- Wear OS 使用
ScalingLazyColumn、TimeText等组件。 - 圆形屏幕需关注安全区域与滚动交互。
@Composable
fun WearHomeScreen() {
Scaffold(
timeText = { TimeText() },
vignette = { Vignette(vignettePosition = VignettePosition.TopAndBottom) },
positionIndicator = { PositionIndicator() }
) {
ScalingLazyColumn {
item { Text("心率", style = MaterialTheme.typography.titleMedium) }
items(steps) { step -> Text(step.toString()) }
}
}
}
手机与手表协同
- 使用
Data Layer API(DataClient、MessageClient)。 - 借助
Kotlinx Serialization统一数据格式。
@Serializable
data class HealthSnapshot(val steps: Int, val heartRate: Int)
suspend fun sendSnapshot(client: MessageClient, nodeId: String, snapshot: HealthSnapshot) {
val payload = Json.encodeToString(snapshot).encodeToByteArray()
client.sendMessage(nodeId, "/health", payload)
}
手表端接收:
class WearMessageService : WearableListenerService() {
override fun onMessageReceived(messageEvent: MessageEvent) {
if (messageEvent.path == "/health") {
val snapshot = Json.decodeFromString<HealthSnapshot>(String(messageEvent.data))
scope.launch { repository.update(snapshot) }
}
}
}
Tiles 与 Complications
class StepsTileService : TileService() {
override fun onTileRequest(requestParams: TileRequest): ListenableFuture<Tile> {
val timeline = Timeline.Builder()
.addTimelineEntry(TimelineEntry.Builder().setLayout(stepsLayout()).build())
.build()
return Futures.immediateFuture(Tile.Builder().setResourcesVersion("1").setTimeline(timeline).build())
}
}
测试与调试
- 使用 Wear OS Emulator Pairing。
adb -e logcat查看手表日志。- 使用 Macrobenchmark 测量功耗与启动。
性能与功耗
- 减少不必要的重绘,使用
remember缓存状态。 - 控制数据同步频率,提供省电模式。
- 使用
WorkManager+NetworkType.NOT_ROAMING控制同步条件。
常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 数据延迟 | 蓝牙或网络连接不稳定 | 在发送前检测节点连接状态,增加重试 |
| UI 截断 | 圆形屏幕未处理安全区域 | 使用 Modifier.fillMaxSize().padding(innerPadding) |
| 编译依赖冲突 | Wear Compose 与手机 Compose 版本不一致 | 对齐 Compose BOM 版本 |
实战案例:WearOS 是”手机 App 的遥控器”
WearOS 开发有个根本认知:手表不是小手机。它的交互是”瞥一眼”(glanceable)——抬腕 3 秒就要看完。所以 WearOS 的 UI 范式和手机完全不同:
- 手机是”列表 + 详情”,手表是”卡片(Tile)+ 一句话”。
- 手机用
LazyColumn滚长列表,手表用ScalingLazyColumn(Wear Compose 的),表盘边缘自动缩放,专为小圆屏优化。 - 输入靠语音和预设回复,别指望用户在 1.5 寸屏上打字。
和手机 App 的协同走 Data Layer API(DataClient/MessageClient),比如手机上下单、手表震动提醒。我在 Flutter 侧没做过 WearOS(Flutter 对 WearOS 的支持很弱,这是原生开发的护城河之一),但思路和蓝牙设备通信是一样的:短连接、少数据、省电第一。
Horologist(Google 官方的 WearOS 组件库)是起手标配,别自己造轮子。它的 MediaPlayer、Tile 模板都是按手表交互范式调好的。
一句话:WearOS 开发先忘掉手机那套,从”抬腕 3 秒”倒推设计。
参考:Android 官方文档《Wear OS》;Horologist GitHub(google/horologist)
总结
动手表端 Compose 需要适应圆形屏幕与功耗限制。通过 Data Layer API 实现手机与手表数据同步,结合 Tiles 提供一目了然的信息展示。合理规划同步频率与布局,可实现跨端一致体验。
