智感握姿快速适配指南
·
HarmonyOS 智感握姿功能开发指南
1. 功能概述
智感握姿功能利用 HarmonyOS 的 Motion 感知能力(SystemCapability.MultimodalAwareness.Motion),实时监测用户握持手机的手势(左手/右手/双手/未握持),可用于根据握持状态动态调整 UI 布局,提升单手操作体验。
应用场景
- 浮动按钮/面板根据握持手自动左右切换
- 单手模式下的 UI 适配
- 游戏手柄布局自动调整
- 阅读应用翻页按钮位置优化
2. 系统要求
| 项目 | 要求 |
|---|---|
| 系统能力 | SystemCapability.MultimodalAwareness.Motion |
| API 模块 | @kit.MultimodalAwarenessKit |
| 设备要求 | 支持 Motion 感知的华为设备 |
3. 握持手状态枚举
根据华为官方 API,握持手状态定义如下:
enum HoldingHandStatus {
NOT_HELD = 0, // 未握持
LEFT_HAND_HELD = 1, // 左手握持
RIGHT_HAND_HELD = 2, // 右手握持
BOTH_HANDS_HELD = 3, // 双手握持
UNKNOWN_STATUS = 16 // 未识别
}
注意:必须严格按照此枚举值定义,数值不能修改。
4. 核心实现
4.0 权限配置
在 module.json5中配置权限
"requestPermissions": [
{
"name" : "ohos.permission.DETECT_GESTURE"
}
]
4.1 握持手状态监听
在 PreviewPage 中实现握持手状态监听:
import { motion } from '@kit.MultimodalAwarenessKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 状态变量:保存当前握持手状态
@State private holdingHandStatus: HoldingHandStatus = HoldingHandStatus.RIGHT_HAND_HELD;
/**
* 开始监听握持手状态
*/
private startHoldingHandMonitoring(): void {
try {
// 关键:事件名称必须是 'holdingHandChanged'(带'd')
motion.on('holdingHandChanged', (data: HoldingHandStatus) => {
console.log(`👋 握持手状态变化: ${data}`);
this.holdingHandStatus = data;
switch (data) {
case HoldingHandStatus.LEFT_HAND_HELD:
console.log('⬅️ 左手握持');
break;
case HoldingHandStatus.RIGHT_HAND_HELD:
console.log('➡️ 右手握持');
break;
case HoldingHandStatus.BOTH_HANDS_HELD:
console.log('🙌 双手握持');
break;
case HoldingHandStatus.NOT_HELD:
console.log('✋ 未握持');
break;
default:
console.log('❓ 未识别');
break;
}
});
console.log('✅ 握持手状态监听已启用');
} catch (err) {
const error = err as BusinessError;
console.error(`❌ 启动监听失败: ${error.code}, ${error.message}`);
}
}
/**
* 停止监听握持手状态
* 重要:必须在页面销毁时调用,否则会造成内存泄漏
*/
private stopHoldingHandMonitoring(): void {
try {
motion.off('holdingHandChanged');
console.log('✅ 已停止握持手状态监听');
} catch (err) {
const error = err as BusinessError;
console.error(`❌ 停止监听失败: ${error.code}, ${error.message}`);
}
}
4.2 页面生命周期管理
@Component
struct MyPage {
@State private holdingHandStatus: HoldingHandStatus = HoldingHandStatus.RIGHT_HAND_HELD;
aboutToAppear(): void {
// 页面显示时启动监听
this.startHoldingHandMonitoring();
}
aboutToDisappear(): void {
// 页面销毁时必须停止监听,防止内存泄漏
this.stopHoldingHandMonitoring();
}
}
5. UI 动态适配示例
5.1 浮动面板左右切换
使用绝对定位 + 动画实现浮动面板位置切换:
@Builder
private buildFloatingPanel(): void {
Column({ space: 12 }) {
// 面板内容(按钮等)
Button({ type: ButtonType.Circle }) {
Text('🔗').fontSize(24)
}
.width(40)
.height(40)
.backgroundColor('#007AFF')
}
.padding(8)
.borderRadius(36)
.shadow({ radius: 16, color: 'rgba(0, 0, 0, 0.15)', offsetY: 4 })
// 关键:根据握持手状态动态调整位置
.position({
x: this.holdingHandStatus === HoldingHandStatus.LEFT_HAND_HELD ? 16 : '100%',
y: '100%'
})
.translate({
// 左手握持:左边距16vp
// 右手握持:右边距16vp(向左偏移100%宽度+16vp)
x: this.holdingHandStatus === HoldingHandStatus.LEFT_HAND_HELD ? 0 : '-100%-16',
y: '-100%-80'
})
// 添加动画效果
.animation({
duration: 300,
curve: Curve.EaseInOut
})
}
5.2 完整页面示例
import { motion } from '@kit.MultimodalAwarenessKit';
import { BusinessError } from '@kit.BasicServicesKit';
enum HoldingHandStatus {
NOT_HELD = 0,
LEFT_HAND_HELD = 1,
RIGHT_HAND_HELD = 2,
BOTH_HANDS_HELD = 3,
UNKNOWN_STATUS = 16
}
@Entry
@Component
struct GestureDemo {
@State private holdingHandStatus: HoldingHandStatus = HoldingHandStatus.RIGHT_HAND_HELD;
aboutToAppear(): void {
this.startHoldingHandMonitoring();
}
aboutToDisappear(): void {
this.stopHoldingHandMonitoring();
}
build() {
Stack({ alignContent: Alignment.BottomStart }) {
// 主内容区域
Column() {
Text('智感握姿演示')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 50 })
Text(`当前状态: ${this.getStatusText()}`)
.fontSize(18)
.margin({ top: 20 })
}
.width('100%')
.height('100%')
// 浮动面板
this.buildFloatingPanel()
}
.width('100%')
.height('100%')
}
private getStatusText(): string {
switch (this.holdingHandStatus) {
case HoldingHandStatus.LEFT_HAND_HELD: return '左手握持';
case HoldingHandStatus.RIGHT_HAND_HELD: return '右手握持';
case HoldingHandStatus.BOTH_HANDS_HELD: return '双手握持';
case HoldingHandStatus.NOT_HELD: return '未握持';
default: return '未识别';
}
}
@Builder
private buildFloatingPanel(): void {
Column({ space: 8 }) {
Button({ type: ButtonType.Circle }) {
Text('⭐').fontSize(20)
}
.width(48)
.height(48)
.backgroundColor('#007AFF')
}
.padding(8)
.borderRadius(32)
.backgroundColor('#FFFFFF')
.shadow({ radius: 12, color: 'rgba(0,0,0,0.15)' })
.position({
x: this.holdingHandStatus === HoldingHandStatus.LEFT_HAND_HELD ? 16 : '100%',
y: '100%'
})
.translate({
x: this.holdingHandStatus === HoldingHandStatus.LEFT_HAND_HELD ? 0 : '-100%-16',
y: '-100%-100'
})
.animation({ duration: 300, curve: Curve.EaseInOut })
}
private startHoldingHandMonitoring(): void {
try {
motion.on('holdingHandChanged', (data: HoldingHandStatus) => {
this.holdingHandStatus = data;
});
} catch (err) {
console.error('启动监听失败:', err);
}
}
private stopHoldingHandMonitoring(): void {
try {
motion.off('holdingHandChanged');
} catch (err) {
console.error('停止监听失败:', err);
}
}
}
6. 注意事项
6.1 API 使用要点
| 要点 | 说明 |
|---|---|
| 事件名称 | 必须是 holdingHandChanged(带'd') |
| 回调参数 | 类型为 HoldingHandStatus 枚举 |
| 注销监听 | 必须在 aboutToDisappear 中调用 motion.off() |
| 状态判断 | 使用 HoldingHandStatus.LEFT_HAND_HELD 等枚举值 |
6.2 内存管理
- 页面销毁时必须调用
motion.off('holdingHandChanged')注销监听 - 避免在监听回调中执行耗时操作
6.3 UI 刷新
- 使用
@State装饰器确保状态变化触发 UI 刷新 - 浮动面板位置变化添加动画效果提升体验
7. 完整流程图
┌───────────────────────────────────────────────┐
│ 页面启动 │
│ aboutToAppear() │
└───────────────────────┬───────────────────────┘
│
▼
┌───────────────────────────────────────────────┐
│ startHoldingHandMonitoring() │
│ motion.on('holdingHandChanged', ...) │
└───────────────────────┬───────────────────────┘
│
▼
┌───────────────────────────────────────────────┐
│ 持续监听中 │
│ 用户切换握持手 → 回调触发 │
│ holdingHandStatus 更新 → UI 自动刷新 │
└───────────────────────┬───────────────────────┘
│
▼
┌───────────────────────────────────────────────┐
│ 页面销毁 │
│ aboutToDisappear() │
│ stopHoldingHandMonitoring() ← 必须调用 │
│ motion.off('holdingHandChanged') │
└───────────────────────────────────────────────┘
8. 常见问题
Q1: 监听不生效?
- 检查事件名称是否为
holdingHandChanged(注意带'd') - 确认设备支持 Motion 感知能力
- 查看日志是否有错误信息
Q2: UI 位置不切换?
- 检查
holdingHandStatus是否使用@State装饰器 - 确认 position/translate 属性正确绑定状态变量
Q3: 页面销毁后仍有日志输出?
- 确认在
aboutToDisappear中调用了motion.off() - 检查是否有其他页面也在监听同一事件
Q4: 如何判断设备是否支持?
- 系统版本大于6.0.0.115的HarmonyOS手机设备均支持智感握姿
更多推荐
所有评论(0)