文章目录
-
- 每日一句正能量
- 导读
- 一、前言:穿戴设备——鸿蒙全场景生态的「贴身入口」
- 二、穿戴设备形态与屏幕类型矩阵
-
- 2.1 圆形屏幕 vs 矩形屏幕的设计哲学
- 三、圆形屏幕适配:从安全区域到 Arc 组件体系
-
- 3.1 圆形屏幕的安全区域与裁切原理
- 3.2 Arc 专用组件体系
-
- ArcButton:贴合圆弧的按钮
- ArcList:支持旋转表冠的弧形列表
- ArcSwiper:弧形轮播组件
- 3.3 圆形列表的渐隐缩放效果
- 四、穿戴设备交互方式设计
-
- 4.1 触屏交互:最基础也最受限
- 4.2 旋转表冠:穿戴设备的「滚轮鼠标」
- 4.3 智慧手势:无接触操控
- 五、传感器数据采集与实时显示
-
- 5.1 心率传感器数据采集
- 5.2 运动状态识别
- 六、AOD 常亮显示与功耗优化
-
- 6.1 AOD(Always On Display)设计规范
- 6.2 功耗优化策略
- 七、分布式数据同步:手表与手机的协同之道
-
- 7.1 分布式同步架构
- 7.2 手表-手机数据同步实现
- 7.3 跨设备接续
- 八、实战案例:「智健康」穿戴应用完整方案
-
- 8.1 项目结构
- 8.2 主页面完整实现
- 九、常见问题与解决方案
-
- Q1:圆形屏幕上文字/按钮被四角裁切怎么办?
- Q2:旋转表冠事件与触摸滚动冲突?
- Q3:AOD 模式下屏幕出现残影或烧屏?
- Q4:传感器数据同步到手机延迟高?
- Q5:穿戴应用内存不足导致崩溃?
- 十、总结

每日一句正能量
每一个普通的改变都将改变普通。 改变不必是惊天动地的——每天读10页书、早睡半小时、散步20分钟,这些事看起来都很“普通”。但当你坚持做下去,它们会累积成一种力量,把你从“普通的自己”变成“不一样的自己”。普通的行为,改变的是“普通”本身。
导读
承接前三篇「分屏模式UI适配」「折叠屏展开收起适配」「平板大屏适配方案」,本文将聚焦鸿蒙生态中最「袖珍」却也最「贴身」的设备形态——智能穿戴。从 1.5 英寸的圆形表盘到长方形手环屏幕,穿戴设备的UI适配不仅是尺寸的缩放,更是对信息密度、交互方式、功耗控制的极限挑战。如何在硬币大小的画布上作画,同时实现与手机的无缝数据协同,是本文的核心命题。
一、前言:穿戴设备——鸿蒙全场景生态的「贴身入口」
智能穿戴设备已从单纯的计时工具进化为全场景健康管理的「贴身入口」。据 IDC 数据显示,2025 年全球智能手表出货量突破 2.5 亿台,其中健康监测、运动管理、分布式协同成为用户核心使用场景。cite🛠web_search:19#0:~:text=据IDC数据显示,2025年全球智能手表出货量突破2.5亿台 在华为全场景智慧生活战略中,手表不再仅仅是手机的「第二屏幕」,而是与手机、平板、智慧屏、车机共同构成「超级终端」的关键节点。
与手机、平板、PC 等设备相比,穿戴设备的UI适配面临独特的挑战:
- 物理尺寸极限:屏幕直径仅 1.2-2.0 英寸,像素密度虽高(326+ DPI),但绝对可视区域极小。
- 屏幕形态多样:圆形(WATCH GT 系列)、方形(WATCH 数字系列)、长方形(手环系列)三种形态并存,圆形屏幕的四角裁切问题尤为突出。
- 交互方式受限:无实体键盘鼠标,依赖触屏、旋转表冠、智慧手势三种输入方式,每种方式都有严格的精度和场景限制。cite🛠web_search:19#4:~:text=旋转表冠交互…表冠是智能穿戴设备特有的一种交互方式,支持按压与旋转
- 功耗极度敏感:每一帧渲染、每一次传感器采样都直接影响续航,「省电」是穿戴开发的第一原则。
- 分布式协同刚需:手表的算力和存储有限,健康数据必须实时同步至手机进行深度分析和长期存储。
本文将从屏幕形态适配、Arc 组件体系、交互方式设计、传感器数据采集、AOD 常亮显示、分布式数据同步、功耗优化七个维度,构建一套完整的穿戴设备 UI 适配方案。
二、穿戴设备形态与屏幕类型矩阵
HarmonyOS 支持的穿戴设备按形态可分为四大类,每类的屏幕特征和适配策略各不相同:

| 智能手表 | WATCH GT 5 Pro | 1.43 英寸 | 圆形 | 466×466 | 326 | 圆形裁切、Arc 组件 |
| 轻量手表 | WATCH FIT 3 | 1.82 英寸 | 方形 | 390×450 | 280 | 紧凑布局、长续航 |
| 智能手环 | Band 10 | 1.47 英寸 | 长方形 | 194×368 | 280 | 垂直信息流、极简交互 |
| 儿童手表 | 儿童手表 5X | 1.6 英寸 | 方形 | 320×360 | 260 | 大图标、语音优先 |
2.1 圆形屏幕 vs 矩形屏幕的设计哲学
圆形屏幕与矩形屏幕在布局重心、交互方式、内容展示上存在本质差异:cite🛠web_search:19#0:~:text=圆形屏幕 vs 矩形屏幕的设计哲学
| 布局重心 | 居中布局、径向菜单 | 垂直流式、边缘对齐 |
| 交互方式 | 旋转表冠、滑动 | 上下滚动、点按 |
| 内容展示 | 避免角落裁剪 | 充分利用边缘空间 |
| 典型场景 | 表盘、环形菜单 | 列表、卡片式信息 |
核心原则:圆形屏幕的实际显示区域是一个内切圆,四角内容会被系统裁切。开发者必须将关键信息放置在屏幕中央的安全区域内,避免靠近边缘。cite🛠web_search:19#0:~:text=应用在实际显示时仅会显示界面设计中的部分圆形界面
三、圆形屏幕适配:从安全区域到 Arc 组件体系
3.1 圆形屏幕的安全区域与裁切原理
圆形屏幕的适配核心在于理解「安全区域」概念。以 466×466 像素的圆形屏幕为例,其有效显示区域是一个直径 466px 的圆,四角(约 90×90px 的区域)会被裁切。cite🛠web_search:19#0:~:text=开发一个宽400高1200的竖长型界面,当上下滑动时,用户只能看到橘色圆圈内部的样式,其余部分不会展示

圆形屏幕适配黄金法则:
3.2 Arc 专用组件体系
HarmonyOS 为圆形屏幕提供了一套专用的 Arc 组件,让 UI 元素自然贴合圆形屏幕的弧线边缘。cite🛠web_search:19#5:~:text=ArcButton 是 HarmonyOS ArkTS 提供的 专用于圆形屏(如智能穿戴设备)上的弧形按钮组件
ArcButton:贴合圆弧的按钮
import {
ArcButton,
ArcButtonOptions,
ArcButtonPosition,
ArcButtonStyleMode
} from '@kit.ArkUI';
@Entry
@Component
struct ArcButtonDemo {
build() {
Stack() {
// 主内容区域
Column() {
Text('心率监测')
.fontSize(20)
.fontColor(Color.White);
Text('72')
.fontSize(48)
.fontWeight(FontWeight.Bold)
.fontColor('#007DFF');
Text('BPM')
.fontSize(14)
.fontColor('#999999');
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.backgroundColor('#1A1A1A');
// 顶部弧形按钮(设置)
ArcButton({
options: new ArcButtonOptions({
label: '设置',
position: ArcButtonPosition.TOP_EDGE,
styleMode: ArcButtonStyleMode.EMPHASIZED_LIGHT,
fontColor: Color.White,
onClick: () => {
router.pushUrl({ url: 'pages/Settings' });
}
})
});
// 底部弧形按钮(开始)
ArcButton({
options: new ArcButtonOptions({
label: '开始',
position: ArcButtonPosition.BOTTOM_EDGE,
styleMode: ArcButtonStyleMode.EMPHASIZED_LIGHT,
backgroundColor: '#007DFF',
fontColor: Color.White,
onClick: () => {
this.startMonitoring();
}
})
});
}
.width('100%')
.height('100%');
}
private startMonitoring(): void {
// 启动监测逻辑
}
}
ArcButtonOptions 核心属性:cite🛠web_search:19#0:~:text=ArcButton的配置选项详解
| position | ArcButtonPosition | 按钮位置 | TOP_EDGE / BOTTOM_EDGE |
| styleMode | ArcButtonStyleMode | 样式模式 | EMPHASIZED_LIGHT / NORMAL / WARNING |
| label | ResourceStr | 按钮文本 | 短标签如「OK」「开始」「返回」 |
| backgroundColor | ColorMetrics | 背景色 | 根据场景选择 |
| fontColor | ColorMetrics | 文字颜色 | Color.White 或深色 |
| shadowEnabled | boolean | 阴影效果 | 圆形屏幕建议开启 |
ArcList:支持旋转表冠的弧形列表
import { ArcList, CrownSensitivity } from '@kit.ArkUI';
@Component
struct HeartRateHistoryList {
@State records: HeartRateRecord[] = [];
build() {
ArcList({ space: 8 }) {
ForEach(this.records, (record: HeartRateRecord) => {
ListItem() {
Row({ space: 8 }) {
Text(`${record.time}`)
.fontSize(12)
.fontColor('#999999');
Text(`${record.value} BPM`)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor(this.getHeartRateColor(record.value));
}
.width('100%')
.padding({ left: 16, right: 16, top: 8, bottom: 8 });
}
}, (record: HeartRateRecord) => record.id);
}
.width('100%')
.height('100%')
.padding({ top: 16, bottom: 16 })
.focusable(true) // 支持获焦
.focusOnTouch(true) // 点击获焦
.defaultFocus(true) // 页面默认焦点
.digitalCrownSensitivity(CrownSensitivity.HIGH) // 表冠响应灵敏度
.scrollBar(BarState.Off) // 隐藏滚动条,保持简洁
.alignListItem(ListItemAlign.Center); // 列表项居中对齐
}
private getHeartRateColor(value: number): string {
if (value > 160) return '#FF3B30';
if (value > 120) return '#FF9500';
return '#007DFF';
}
}
关键配置:digitalCrownSensitivity 用于调节旋转表冠的响应灵敏度。列表项数据较多时设置为 HIGH,数据较少时设置为 LOW,避免过度滚动。cite🛠web_search:19#8:~:text=还可以通过 digitalCrownSensitivity 属性设置表冠响应事件的灵敏度
ArcSwiper:弧形轮播组件
import {
ArcSwiper,
ArcSwiperController,
ArcButton,
ArcButtonOptions,
ArcButtonPosition,
ArcButtonStyleMode
} from '@kit.ArkUI';
@Entry
@Component
struct SportModeSelector {
private swiperController: ArcSwiperController = new ArcSwiperController();
@State modes: SportMode[] = [
{ name: '跑步', icon: $r('app.media.run') },
{ name: '骑行', icon: $r('app.media.cycle') },
{ name: '游泳', icon: $r('app.media.swim') },
{ name: '登山', icon: $r('app.media.hike') }
];
build() {
Stack() {
// 弧形轮播
ArcSwiper(this.swiperController) {
ForEach(this.modes, (mode: SportMode) => {
Column() {
Image(mode.icon)
.width(48)
.height(48);
Text(mode.name)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ top: 8 });
}
.width(200)
.height(200)
.justifyContent(FlexAlign.Center);
});
}
.vertical(true) // 垂直方向轮播
.indicator(false) // 隐藏指示器,保持简洁
.onAnimationStart((index: number, targetIndex: number) => {
console.info(`从模式 ${index} 切换到 ${targetIndex}`);
});
// 上下控制按钮
Column() {
ArcButton({
options: new ArcButtonOptions({
label: '上一个',
position: ArcButtonPosition.TOP_EDGE,
styleMode: ArcButtonStyleMode.EMPHASIZED_LIGHT,
onClick: () => this.swiperController.showPrevious()
})
});
Blank();
ArcButton({
options: new ArcButtonOptions({
label: '下一个',
position: ArcButtonPosition.BOTTOM_EDGE,
styleMode: ArcButtonStyleMode.EMPHASIZED_LIGHT,
onClick: () => this.swiperController.showNext()
})
});
}
.width('100%')
.height('100%');
}
.width('100%')
.height('100%');
}
}
3.3 圆形列表的渐隐缩放效果
在圆形屏幕上,列表项滚动到边缘时应当逐渐缩小并渐隐,以契合圆形边界的视觉效果:cite🛠web_search:19#7:~:text=圆形列表两端逐渐缩小、渐隐效果
// 圆形列表项的渐隐缩放计算
interface ScaleRange {
MIN: number;
MAX: number;
}
interface ScaleZone {
TOP: number; // 顶部渐隐区起始Y坐标
CENTER: number[]; // 中心可见区域 [起始, 结束]
BOTTOM: number; // 底部渐隐区结束Y坐标
}
@Component
struct CircularFadeList {
@State listItemScale: number[] = [];
private listScroller: ListScroller = new ListScroller();
private readonly SCALE_RANGE: ScaleRange = { MIN: 1, MAX: 2 };
private readonly SCROLL_ZONE: ScaleZone = {
TOP: 0,
CENTER: [86.5, 146.5],
BOTTOM: 233
};
aboutToAppear(): void {
// 初始化缩放数组
this.listItemScale = new Array(10).fill(this.SCALE_RANGE.MAX);
}
private calculateScale(childCenter: number): number {
let scale = this.SCALE_RANGE.MAX;
// 顶部渐隐区
if (this.SCROLL_ZONE.TOP <= childCenter &&
childCenter <= this.SCROLL_ZONE.CENTER[0]) {
const progress = Math.min(1,
(childCenter – this.SCROLL_ZONE.TOP) /
(this.SCROLL_ZONE.CENTER[0] – this.SCROLL_ZONE.TOP));
scale = this.SCALE_RANGE.MIN +
(this.SCALE_RANGE.MAX – this.SCALE_RANGE.MIN) * progress;
}
// 中心区域保持最大
else if (childCenter >= this.SCROLL_ZONE.CENTER[0] &&
childCenter <= this.SCROLL_ZONE.CENTER[1]) {
scale = this.SCALE_RANGE.MAX;
}
// 底部渐隐区
else if (this.SCROLL_ZONE.CENTER[1] <= childCenter &&
childCenter <= this.SCROLL_ZONE.BOTTOM) {
const progress = Math.min(1,
(childCenter – this.SCROLL_ZONE.CENTER[1]) /
(this.SCROLL_ZONE.BOTTOM – this.SCROLL_ZONE.CENTER[1]));
scale = this.SCALE_RANGE.MAX –
(this.SCALE_RANGE.MAX – this.SCALE_RANGE.MIN) * progress;
}
return Math.min(this.SCALE_RANGE.MAX,
Math.max(this.SCALE_RANGE.MIN, scale));
}
build() {
List({ scroller: this.listScroller, space: 4 }) {
ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (item: number, index: number) => {
ListItem() {
Row() {
Text(`项目 ${item}`)
.fontSize(14)
.fontColor(Color.White);
}
.width('50%')
.height(33 * this.listItemScale[index])
.padding({ left: 11, right: 11 })
.backgroundColor('#1FFFFFFF')
.borderRadius(33)
.scale({
x: this.listItemScale[index],
y: this.listItemScale[index]
});
}
.onAreaChange((oldValue, newValue) => {
const itemHeight = newValue.height as number;
const centerY = (newValue.position.y as number) + itemHeight / 2;
this.listItemScale[index] = this.calculateScale(centerY);
});
});
}
.height('100%')
.width('100%')
.padding({ left: 4, right: 4 })
.scrollBar(BarState.Off)
.alignListItem(ListItemAlign.Center);
}
}
四、穿戴设备交互方式设计
穿戴设备的交互方式与手机截然不同,必须在极小的输入空间内实现高效操作。HarmonyOS 穿戴设备支持三种核心交互方式:

4.1 触屏交互:最基础也最受限
触屏是穿戴设备最直观的交互方式,但受限于屏幕尺寸,必须遵循严格的触控规范:cite🛠web_search:19#0:~:text=最小触控区域:96px × 96px(约9mm × 9mm)
// 触控热区规范组件
@Component
struct TouchSafeButton {
@Prop label: string;
@Prop onClick: () => void;
@State bgColor: string = '#007DFF';
build() {
Button(this.label)
.width(96) // 最小触控宽度 96px
.height(96) // 最小触控高度 96px
.backgroundColor(this.bgColor)
.fontColor(Color.White)
.fontSize(16)
.borderRadius(48) // 圆形按钮,视觉友好
.onClick(() => {
// 触觉反馈
vibrator.startVibration({
type: 'time',
duration: 50
}, { id: 0 });
this.onClick();
})
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down) {
this.bgColor = '#0056B3'; // 按下状态
} else if (event.type === TouchType.Up) {
this.bgColor = '#007DFF'; // 恢复状态
}
});
}
}
触屏交互设计要点:
- 最小热区 96×96px:约 9mm×9mm,符合成人手指平均宽度。
- 重要功能放下半区:手表佩戴在手腕上,拇指更易触达屏幕下半部分。
- 减少页面跳转:优先使用弹窗、侧边栏展示次要信息,避免深层导航。
- 防误触设计:边缘区域(距屏幕边缘 16px 内)不放置可交互元素。
4.2 旋转表冠:穿戴设备的「滚轮鼠标」
旋转表冠是智能手表独有的交互方式,支持按压与旋转两种操作,可触发滚动、缩放、调节数据等交互。cite🛠web_search:19#4:~:text=旋转表冠交互…表冠是智能穿戴设备特有的一种交互方式,支持按压与旋转
// 表冠事件监听与处理
@Component
struct CrownAwarePage {
@State scrollOffset: number = 0;
@State selectedIndex: number = 0;
private scroller: Scroller = new Scroller();
build() {
Column() {
// 内容区域
Scroll(this.scroller) {
Column({ space: 16 }) {
ForEach(this.menuItems, (item: MenuItem, index: number) => {
Row() {
Image(item.icon)
.width(32)
.height(32);
Text(item.label)
.fontSize(16)
.fontColor(index === this.selectedIndex ? '#007DFF' : Color.White);
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor(index === this.selectedIndex ? '#1A1A1A' : 'transparent');
});
}
.width('100%');
}
.width('100%')
.height('100%')
.scrollable(ScrollDirection.Vertical)
.scrollBar(BarState.Off);
}
.width('100%')
.height('100%')
.backgroundColor('#000000')
.onKeyEvent((event: KeyEvent) => {
// 表冠旋转映射为方向键事件
if (event.keyCode === KeyCode.KEY_DPAD_UP) {
this.selectedIndex = Math.max(0, this.selectedIndex – 1);
this.scrollToItem(this.selectedIndex);
return true;
}
if (event.keyCode === KeyCode.KEY_DPAD_DOWN) {
this.selectedIndex = Math.min(this.menuItems.length – 1, this.selectedIndex + 1);
this.scrollToItem(this.selectedIndex);
return true;
}
// 表冠按压映射为确认键
if (event.keyCode === KeyCode.KEY_ENTER) {
this.menuItems[this.selectedIndex].onClick();
return true;
}
return false;
});
}
private scrollToItem(index: number): void {
const itemHeight = 56;
this.scroller.scrollTo({
xOffset: 0,
yOffset: index * itemHeight
});
}
}
4.3 智慧手势:无接触操控
智慧手势是穿戴设备的独特交互模式,用户可通过手指轻敲、指关节滑动、手腕翻转等动作实现控制,在运动中或双手不便触屏时尤为实用。cite🛠web_search:19#4:~:text=智慧手势…用户可以利用手指轻敲和指关节滑动实现控制和切换选择诉求
// 智慧手势监听
import sensor from '@ohos.sensor';
export class GestureManager {
private static instance: GestureManager;
private gestureListeners: Array<(gesture: string) => void> = [];
static getInstance(): GestureManager {
if (!GestureManager.instance) {
GestureManager.instance = new GestureManager();
}
return GestureManager.instance;
}
startMonitoring(): void {
// 监听加速度传感器,识别手势动作
sensor.on(sensor.SensorType.SENSOR_TYPE_ID_ACCELEROMETER, (data) => {
const { x, y, z } = data.values;
const magnitude = Math.sqrt(x * x + y * y + z * z);
// 手腕翻转检测(z轴剧烈变化)
if (Math.abs(z) > 15) {
this.notifyListeners('wrist_flip');
}
// 覆盖熄屏检测(y轴持续向下)
if (y < –9 && Math.abs(x) < 2 && Math.abs(z) < 2) {
this.notifyListeners('cover_screen');
}
}, { interval: 100 }); // 100ms 采样间隔
}
stopMonitoring(): void {
sensor.off(sensor.SensorType.SENSOR_TYPE_ID_ACCELEROMETER);
}
addListener(callback: (gesture: string) => void): void {
this.gestureListeners.push(callback);
}
private notifyListeners(gesture: string): void {
this.gestureListeners.forEach(cb => cb(gesture));
}
}
// 使用示例
@Component
struct GestureAwarePage {
@State currentGesture: string = '无';
aboutToAppear(): void {
const manager = GestureManager.getInstance();
manager.addListener((gesture) => {
this.currentGesture = gesture;
switch (gesture) {
case 'wrist_flip':
// 手腕翻转:切换下一首歌曲
this.nextTrack();
break;
case 'cover_screen':
// 覆盖屏幕:立即熄屏
this.turnOffScreen();
break;
}
});
manager.startMonitoring();
}
aboutToDisappear(): void {
GestureManager.getInstance().stopMonitoring();
}
private nextTrack(): void { /* 切歌逻辑 */ }
private turnOffScreen(): void { /* 熄屏逻辑 */ }
}
五、传感器数据采集与实时显示
穿戴设备的核心价值在于健康监测,心率、血氧、步数等传感器数据的实时采集和展示是应用开发的重中之重。cite🛠web_search:19#0:~:text=传感器数据采集:心率、步数等健康数据的获取与处理
5.1 心率传感器数据采集
import sensor from '@ohos.sensor';
@Component
struct HeartRateMonitor {
@State heartRate: number = 0;
@State isMonitoring: boolean = false;
@State motionState: string = '静息';
private motionDetector: MotionStateDetector;
private alertSystem: HeartRateAlertSystem;
aboutToAppear(): void {
this.motionDetector = new MotionStateDetector((state) => {
this.onMotionStateChange(state);
});
this.alertSystem = new HeartRateAlertSystem();
}
startMonitoring(): void {
this.isMonitoring = true;
// 启动心率监测
// 运动场景:50Hz 高频采样(interval: 20ms)
// 静息场景:10Hz 低频采样(interval: 100ms)
const samplingInterval = this.motionState === '跑步' ? 20 : 100;
sensor.on(sensor.SensorType.SENSOR_TYPE_ID_HEART_RATE, (data) => {
this.heartRate = data.values[0];
// 异常检测:心率>180或<50时触发震动提醒
if (this.heartRate > 180 || this.heartRate < 50) {
this.alertSystem.triggerAlert(this.heartRate);
}
}, { interval: samplingInterval });
}
stopMonitoring(): void {
this.isMonitoring = false;
sensor.off(sensor.SensorType.SENSOR_TYPE_ID_HEART_RATE);
}
private onMotionStateChange(state: string): void {
const stateMap: Record<string, string> = {
'rest': '静息',
'walking': '步行',
'running': '跑步'
};
this.motionState = stateMap[state] || state;
// 运动状态变化时调整采样频率
if (this.isMonitoring) {
this.stopMonitoring();
this.startMonitoring();
}
}
build() {
Column({ space: 12 }) {
// 状态栏
Row() {
Text(this.motionState)
.fontSize(14)
.fontColor('#999999');
}
.width('100%')
.padding(12);
// 心率大数字显示
Column() {
Text('实时心率')
.fontSize(12)
.fontColor('#666666');
Text(`${this.heartRate}`)
.fontSize(56)
.fontWeight(FontWeight.Bold)
.fontColor(this.getHeartRateColor());
Text('BPM')
.fontSize(14)
.fontColor('#666666');
}
.width('100%')
.layoutWeight(1)
.justifyContent(FlexAlign.Center);
// 控制按钮
ArcButton({
options: new ArcButtonOptions({
label: this.isMonitoring ? '停止' : '开始',
position: ArcButtonPosition.BOTTOM_EDGE,
styleMode: ArcButtonStyleMode.EMPHASIZED_LIGHT,
backgroundColor: this.isMonitoring ? '#FF3B30' : '#007DFF',
fontColor: Color.White,
onClick: () => {
if (this.isMonitoring) {
this.stopMonitoring();
} else {
this.startMonitoring();
}
}
})
});
}
.width('100%')
.height('100%')
.backgroundColor('#1A1A1A');
}
private getHeartRateColor(): string {
if (this.heartRate > 160) return '#FF3B30';
if (this.heartRate > 120) return '#FF9500';
return '#007DFF';
}
}
5.2 运动状态识别
通过加速度传感器识别用户的运动状态,动态调整采样策略:
export class MotionStateDetector {
private callback: (state: string) => void;
private currentState: string = 'rest';
private readonly THRESHOLD_WALKING = 5.0; // 步行加速度阈值
private readonly THRESHOLD_RUNNING = 12.0; // 跑步加速度阈值
constructor(callback: (state: string) => void) {
this.callback = callback;
}
startDetection(): void {
sensor.on(sensor.SensorType.SENSOR_TYPE_ID_ACCELEROMETER, (data) => {
const { x, y, z } = data.values;
const magnitude = Math.sqrt(x * x + y * y + z * z);
let newState = 'rest';
if (magnitude > this.THRESHOLD_RUNNING) {
newState = 'running';
} else if (magnitude > this.THRESHOLD_WALKING) {
newState = 'walking';
}
if (newState !== this.currentState) {
this.currentState = newState;
this.callback(newState);
}
}, { interval: 200 }); // 200ms 采样间隔
}
stopDetection(): void {
sensor.off(sensor.SensorType.SENSOR_TYPE_ID_ACCELEROMETER);
}
}
六、AOD 常亮显示与功耗优化
6.1 AOD(Always On Display)设计规范
AOD 是智能手表的核心功能,允许用户在熄屏状态下查看时间、心率等关键信息。HarmonyOS 要求 AOD 界面必须极度精简,以降低 OLED 屏幕的功耗。cite🛠web_search:19#0:~:text=手表开发的第一原则是"省电"

AOD 设计三原则:
// AOD 界面组件
@Component
struct AODFace {
@State currentTime: string = '';
@State currentDate: string = '';
@State heartRate: number = 72;
private timer: number = –1;
aboutToAppear(): void {
this.updateTime();
// AOD 模式下每分钟更新一次
this.timer = setInterval(() => {
this.updateTime();
}, 60000);
}
aboutToDisappear(): void {
clearInterval(this.timer);
}
private updateTime(): void {
const now = new Date();
this.currentTime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
this.currentDate = `${now.getMonth() + 1}月${now.getDate()}日`;
}
build() {
Column({ space: 8 }) {
// 时间(最大字号,居中)
Text(this.currentTime)
.fontSize(48)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White);
// 日期(次要信息)
Text(this.currentDate)
.fontSize(14)
.fontColor('#666666');
// 心率(极简显示)
Row({ space: 4 }) {
Image($r('app.media.heart_icon'))
.width(12)
.height(12)
.fillColor('#FF3B30');
Text(`${this.heartRate}`)
.fontSize(12)
.fontColor('#999999');
}
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.backgroundColor('#000000'); // 纯黑背景,OLED不发光
}
}
6.2 功耗优化策略
穿戴设备的续航是用户体验的核心指标,开发者必须在每一行代码中考虑功耗:
// 功耗管理器
export class PowerManager {
private static readonly HIGH_FREQ_INTERVAL = 20; // 50Hz,运动场景
private static readonly LOW_FREQ_INTERVAL = 100; // 10Hz,静息场景
private static readonly BACKGROUND_INTERVAL = 500; // 2Hz,后台场景
/**
* 根据场景动态调整传感器采样频率
*/
static getOptimalSamplingInterval(scene: SensorScene): number {
switch (scene) {
case SensorScene.ACTIVE_SPORT:
return this.HIGH_FREQ_INTERVAL;
case SensorScene.DAILY_ACTIVITY:
return this.LOW_FREQ_INTERVAL;
case SensorScene.BACKGROUND:
return this.BACKGROUND_INTERVAL;
default:
return this.LOW_FREQ_INTERVAL;
}
}
/**
* 批量处理后台任务,减少唤醒次数
*/
static scheduleBackgroundTask(task: () => void, interval: number): void {
// 使用 WorkScheduler 而非 setInterval,系统会自动合并任务
const workInfo: workScheduler.WorkInfo = {
workId: Date.now(),
bundleName: getContext().applicationInfo.name,
abilityName: 'BackgroundTaskAbility',
parameters: {
taskType: 'data_sync'
}
};
workScheduler.startWork(workInfo);
}
}
export enum SensorScene {
ACTIVE_SPORT, // 主动运动
DAILY_ACTIVITY, // 日常活动
BACKGROUND // 后台运行
}
功耗优化清单:
| 传感器采样 | 运动 50Hz / 静息 10Hz / 后台 2Hz 动态调节 | 降低 60% 传感器功耗 |
| 列表渲染 | 使用 LazyForEach 懒加载,避免一次性渲染全部数据 | 降低 40% 渲染功耗 |
| 动画优化 | 使用属性动画替代帧动画,减少重绘次数 | 降低 30% GPU 功耗 |
| 深色模式 | AOD 和主界面优先使用深色背景 | OLED 屏幕降低 50% 显示功耗 |
| 后台任务 | 使用 WorkScheduler 批量处理,减少 CPU 唤醒 | 降低 25% 后台功耗 |
七、分布式数据同步:手表与手机的协同之道
7.1 分布式同步架构
鸿蒙的分布式能力让手表摆脱「配件」定位,实现与手机等设备的深度协同。核心技术架构包括:cite🛠web_search:19#0:~:text=分布式软总线(Distributed Soft Bus)…设备间的"神经网络",实现低延迟发现与通信

7.2 手表-手机数据同步实现
import { distributedData } from '@kit.DistributedServiceKit';
export class HealthDataSyncManager {
private kvManager: distributedData.KVManager | null = null;
private kvStore: distributedData.SingleKVStore | null = null;
private readonly STORE_ID = 'health_data_sync';
async init(context: Context): Promise<void> {
// 创建 KV 管理器
const config: distributedData.KVManagerConfig = {
bundleName: context.applicationInfo.name,
context: context
};
this.kvManager = distributedData.createKVManager(config);
// 创建分布式 KV 存储
const options: distributedData.Options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true, // 自动同步到配对设备
kvStoreType: distributedData.KVStoreType.SINGLE_VERSION,
securityLevel: distributedData.SecurityLevel.S1
};
this.kvStore = await this.kvManager.getKVStore(this.STORE_ID, options);
// 监听数据变化(手机端修改后手表自动更新)
this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => {
console.info(`[Sync] 收到同步数据: ${JSON.stringify(data)}`);
this.handleSyncData(data);
});
}
/**
* 保存心率数据并同步至手机
*/
async saveHeartRateData(record: HeartRateRecord): Promise<void> {
if (!this.kvStore) return;
const key = `hr_${record.timestamp}`;
const value = JSON.stringify(record);
await this.kvStore.put(key, value);
console.info(`[Sync] 心率数据已保存并同步: ${key}`);
}
/**
* 批量同步运动记录
*/
async batchSyncSportRecords(records: SportRecord[]): Promise<void> {
if (!this.kvStore) return;
// 批量写入,减少同步次数
for (const record of records) {
const key = `sport_${record.id}`;
await this.kvStore.put(key, JSON.stringify(record));
}
// 触发一次同步
await this.kvStore.sync();
console.info(`[Sync] 批量同步 ${records.length} 条运动记录`);
}
/**
* 从手机获取历史数据
*/
async getHistoryData(startTime: number, endTime: number): Promise<HeartRateRecord[]> {
if (!this.kvStore) return [];
const result: HeartRateRecord[] = [];
const entries = await this.kvStore.getEntries('hr_'); // 前缀查询
for (const entry of entries) {
const record = JSON.parse(entry.value.toString()) as HeartRateRecord;
if (record.timestamp >= startTime && record.timestamp <= endTime) {
result.push(record);
}
}
return result.sort((a, b) => a.timestamp – b.timestamp);
}
private handleSyncData(data: distributedData.ChangeNotification): void {
// 处理同步过来的数据变更
for (const entry of data.insertEntries) {
console.info(`[Sync] 新增数据: ${entry.key}`);
}
for (const entry of data.updateEntries) {
console.info(`[Sync] 更新数据: ${entry.key}`);
}
}
async close(): Promise<void> {
if (this.kvStore) {
this.kvStore.off('dataChange');
await this.kvStore.close();
}
if (this.kvManager) {
await this.kvManager.close();
}
}
}
7.3 跨设备接续
HarmonyOS 支持应用任务在设备间无缝流转。例如,用户在手表上启动 eSIM 激活,配对手机会自动打开对应界面继续操作。cite🛠web_search:19#4:~:text=跨设备接续…在手表上启动 eSIM 激活,会在配对手机上触发打开对应的界面
// 跨设备接续实现
import { continuation } from '@kit.ContinuationManagerKit';
export class CrossDeviceContinuation {
/**
* 将当前任务迁移至手机
*/
async migrateToPhone(currentTask: TaskInfo): Promise<void> {
const want: Want = {
bundleName: 'com.example.healthapp',
abilityName: 'PhoneDetailAbility',
parameters: {
taskData: JSON.stringify(currentTask),
continuationType: 'detail_view'
}
};
// 注册接续回调
continuation.registerContinuation(
continuation.ContinuationMode.SINGLE,
{
onConnected: (deviceId: string) => {
console.info(`[Continuation] 已连接设备: ${deviceId}`);
},
onDisconnected: (deviceId: string) => {
console.info(`[Continuation] 设备断开: ${deviceId}`);
},
onCompleted: (code: number) => {
if (code === 0) {
console.info('[Continuation] 任务迁移成功');
} else {
console.error(`[Continuation] 任务迁移失败: ${code}`);
}
}
}
);
// 启动接续
await continuation.startContinuationDeviceManager(want);
}
}
八、实战案例:「智健康」穿戴应用完整方案
8.1 项目结构
entry/src/main/ets/
├── entryability/
│ └── EntryAbility.ets # 入口Ability
├── pages/
│ ├── Index.ets # 主页面(心率监测)
│ ├── AODPage.ets # AOD常亮显示页面
│ ├── HistoryPage.ets # 历史记录页面
│ └── SettingsPage.ets # 设置页面
├── components/
│ ├── ArcHeartRateDisplay.ets # 弧形心率显示组件
│ ├── CircularFadeList.ets # 圆形渐隐列表
│ ├── TouchSafeButton.ets # 安全触控按钮
│ └── SportModeSelector.ets # 运动模式选择器
├── core/
│ ├── SensorManager.ets # 传感器管理器
│ ├── PowerManager.ets # 功耗管理器
│ ├── GestureManager.ets # 手势管理器
│ └── HealthDataSyncManager.ets # 数据同步管理器
├── models/
│ ├── HeartRateRecord.ets # 心率记录模型
│ └── SportRecord.ets # 运动记录模型
└── utils/
└── CircularLayoutUtil.ets # 圆形布局工具类
8.2 主页面完整实现
// pages/Index.ets
import { SensorManager } from '../core/SensorManager';
import { HealthDataSyncManager } from '../core/HealthDataSyncManager';
import { PowerManager, SensorScene } from '../core/PowerManager';
@Entry
@Component
struct Index {
@State heartRate: number = 0;
@State isMonitoring: boolean = false;
@State motionState: string = '静息';
@State currentGesture: string = '';
private sensorManager: SensorManager = new SensorManager();
private syncManager: HealthDataSyncManager = new HealthDataSyncManager();
private historyRecords: HeartRateRecord[] = [];
async aboutToAppear(): Promise<void> {
await this.syncManager.init(getContext());
// 注册运动状态监听
this.sensorManager.onMotionStateChange((state) => {
this.motionState = state;
// 动态调整采样频率
if (this.isMonitoring) {
const scene = state === '跑步' ? SensorScene.ACTIVE_SPORT : SensorScene.DAILY_ACTIVITY;
this.sensorManager.setSamplingInterval(PowerManager.getOptimalSamplingInterval(scene));
}
});
// 注册手势监听
GestureManager.getInstance().addListener((gesture) => {
this.currentGesture = gesture;
if (gesture === 'cover_screen' && this.isMonitoring) {
this.stopMonitoring();
}
});
GestureManager.getInstance().startMonitoring();
}
aboutToDisappear(): void {
this.sensorManager.release();
GestureManager.getInstance().stopMonitoring();
this.syncManager.close();
}
startMonitoring(): void {
this.isMonitoring = true;
this.sensorManager.startHeartRateMonitoring((hr) => {
this.heartRate = hr;
// 保存记录
const record: HeartRateRecord = {
id: Date.now().toString(),
value: hr,
timestamp: Date.now(),
motionState: this.motionState
};
this.historyRecords.push(record);
// 每10条记录批量同步一次
if (this.historyRecords.length >= 10) {
this.syncManager.batchSyncHeartRate(this.historyRecords);
this.historyRecords = [];
}
});
}
stopMonitoring(): void {
this.isMonitoring = false;
this.sensorManager.stopHeartRateMonitoring();
// 同步剩余记录
if (this.historyRecords.length > 0) {
this.syncManager.batchSyncHeartRate(this.historyRecords);
this.historyRecords = [];
}
}
build() {
Stack() {
// 主内容区
Column({ space: 8 }) {
// 状态栏
Row() {
Text(this.motionState)
.fontSize(12)
.fontColor('#666666');
Blank();
Text(this.currentGesture)
.fontSize(10)
.fontColor('#999999');
}
.width('100%')
.padding({ left: 16, right: 16, top: 12 });
// 心率显示
Column() {
Text('实时心率')
.fontSize(12)
.fontColor('#666666');
Text(`${this.heartRate}`)
.fontSize(56)
.fontWeight(FontWeight.Bold)
.fontColor(this.getHeartRateColor());
Text('BPM')
.fontSize(14)
.fontColor('#666666');
}
.layoutWeight(1)
.justifyContent(FlexAlign.Center);
// 心率趋势简图
HeartRateMiniChart({ records: this.historyRecords })
.width('80%')
.height(40);
}
.width('100%')
.height('100%')
.backgroundColor('#1A1A1A');
// 顶部弧形按钮(设置)
ArcButton({
options: new ArcButtonOptions({
label: '设置',
position: ArcButtonPosition.TOP_EDGE,
styleMode: ArcButtonStyleMode.NORMAL,
fontColor: Color.White,
onClick: () => router.pushUrl({ url: 'pages/SettingsPage' })
})
});
// 底部弧形按钮(开始/停止)
ArcButton({
options: new ArcButtonOptions({
label: this.isMonitoring ? '停止' : '开始',
position: ArcButtonPosition.BOTTOM_EDGE,
styleMode: ArcButtonStyleMode.EMPHASIZED_LIGHT,
backgroundColor: this.isMonitoring ? '#FF3B30' : '#007DFF',
fontColor: Color.White,
onClick: () => {
if (this.isMonitoring) {
this.stopMonitoring();
} else {
this.startMonitoring();
}
}
})
});
}
.width('100%')
.height('100%');
}
private getHeartRateColor(): string {
if (this.heartRate > 160) return '#FF3B30';
if (this.heartRate > 120) return '#FF9500';
return '#007DFF';
}
}
九、常见问题与解决方案
Q1:圆形屏幕上文字/按钮被四角裁切怎么办?
解决方案:
Q2:旋转表冠事件与触摸滚动冲突?
解决方案:
Q3:AOD 模式下屏幕出现残影或烧屏?
解决方案:
Q4:传感器数据同步到手机延迟高?
解决方案:
Q5:穿戴应用内存不足导致崩溃?
解决方案:
十、总结
HarmonyOS 穿戴设备 UI 适配是一项在极端约束条件下追求极致体验的工程。本文从屏幕形态、组件体系、交互设计、数据采集、AOD 显示、分布式同步、功耗优化七个维度,构建了一套完整的适配方案:
随着 HarmonyOS 在 WATCH GT 系列、WATCH 数字系列、手环等穿戴设备上的持续深耕,穿戴应用开发正从「被动监测」向「主动感知 + 智能决策」演进。掌握本文所述的适配策略,将帮助你的应用在腕间方寸之地创造无限可能。
转载自:https://blog.csdn.net/u014727709/article/details/163482817 欢迎 👍点赞✍评论⭐收藏,欢迎指正



