一、效果说明

通过 MultimodalAwarenessKit 订阅用户握持手机的姿态,实时感知四种状态并驱动 UI 布局自适应:

握姿 快捷操作栏(ActionBar) FAB 悬浮按钮
左手握持 靠左对齐 移至左侧
右手握持 靠右对齐 移至右侧
双手握持 居中对齐 移至右侧
未握持 / 未知 居中对齐 移至右侧

二、实现流程

Step 1  声明权限 → module.json5
        ↓
Step 2  导入模块 → import { motion } from '@kit.MultimodalAwarenessKit'Step 3  定义响应式数据模型 → @ObservedV2 class GripState
        用 @Trace 追踪 status,@Computed 派生布局属性
        ↓
Step 4  页面生命周期中订阅/取消订阅
        aboutToAppear  → motion.on('holdingHandChanged', callback)
        aboutToDisappear → motion.off('holdingHandChanged', callback)
        ↓
Step 5  回调中赋值 grip.status,@Trace 触发最小范围重渲染
        ↓
Step 6  UI 组件读取 @Computed 派生的布局属性(actionAlign / fabAlign)
        自动完成对齐切换 + 过渡动画

三、关键代码讲解

1. 权限声明(module.json5

"requestPermissions": [
  {
    "name": "ohos.permission.DETECT_GESTURE",
    "reason": "$string:permission_detect_gesture_reason",
    "usedScene": {
      "abilities": ["EntryAbility"],
      "when": "inuse"   // 仅前台使用期间需要
    }
  }
]

注意权限选择:

  • holdingHandChanged(四状态,含双手)→ 必须使用 ohos.permission.DETECT_GESTURE
  • operatingHandChanged(仅左/右,API 15+)→ 可用 ohos.permission.ACTIVITY_MOTION

两者均为 user_grant 权限,首次运行时系统自动弹出授权弹框。


2. 导入模块

import { motion } from '@kit.MultimodalAwarenessKit';

3. 响应式数据模型(核心设计)

@ObservedV2
class GripState {
  // @Trace:仅 status 字段变化时触发依赖它的组件最小范围重渲染
  @Trace status: motion.HoldingHandStatus = motion.HoldingHandStatus.UNKNOWN_STATUS;

  // @Computed:status 不变则不重新计算,结果自动缓存
  @Computed
  get actionAlign(): HorizontalAlign {
    if (this.status === motion.HoldingHandStatus.LEFT_HAND_HELD) {
      return HorizontalAlign.Start;
    }
    if (this.status === motion.HoldingHandStatus.RIGHT_HAND_HELD) {
      return HorizontalAlign.End;
    }
    return HorizontalAlign.Center;
  }

  @Computed
  get fabAlign(): HorizontalAlign {
    if (this.status === motion.HoldingHandStatus.LEFT_HAND_HELD) {
      return HorizontalAlign.Start;
    }
    return HorizontalAlign.End;  // 右手/双手/未握持均靠右
  }
}

为什么用 @ObservedV2 而不是 @Observed(V1)?
项目基于 HarmonyOS 6.1(API 23),全面采用 State Management V2。@Trace 提供字段级细粒度追踪,只有 status 改变时才重渲染,避免整对象比较的额外开销。


4. 订阅与取消订阅(生命周期配对)

// ① 将回调保存为类成员,保证 on/off 操作的是同一个函数引用
private readonly gripCallback: (status: motion.HoldingHandStatus) => void =
  (status: motion.HoldingHandStatus): void => {
    this.grip.status = status;   // 赋值即触发 @Trace 响应链
  };

// ② 页面出现时订阅
aboutToAppear(): void {
  motion.on('holdingHandChanged', this.gripCallback);
}

// ③ 页面销毁时精确取消,传入同一引用
aboutToDisappear(): void {
  motion.off('holdingHandChanged', this.gripCallback);
}

两个关键细节:

  • gripCallback 必须保存引用,不能写成 motion.off('holdingHandChanged') 无参形式——无参会移除该事件的所有订阅者,在多组件场景下会误伤其他监听。
  • aboutToAppear / aboutToDisappear 必须成对,否则页面销毁后回调仍执行,向已销毁的状态对象写值,引发崩溃。

5. 快捷操作栏对齐(ActionBar 组件)

// @Param 接收父级传入的对齐方向,@Trace 变化自动穿透刷新
@ComponentV2
struct ActionBar {
  @Param _align: HorizontalAlign = HorizontalAlign.Center;

  build() {
    Flex({
      justifyContent: this._align === HorizontalAlign.Start
        ? FlexAlign.Start
        : this._align === HorizontalAlign.End
          ? FlexAlign.End
          : FlexAlign.Center
    }) { ... }
    .animation({ duration: 300, curve: Curve.EaseInOut }) // 对齐切换过渡
  }
}

// 父页面传入
ActionBar({ _align: this.grip.actionAlign })

FlexjustifyContent布局属性,改变时子元素真实移动,并触发 .animation() 驱动的过渡动效。


6. FAB 悬浮按钮随握姿移动

Column() {
  this.fab()   // FAB 自身不需要任何 offset
}
.width('100%')
.padding({ bottom: 32, left: 24, right: 24 })
.alignItems(this.grip.fabAlign)                       // 布局属性,真实改变位置
.animation({ duration: 300, curve: Curve.EaseInOut }) // 动画加在容器上

offset vs alignItems 的本质区别:
offset 在渲染阶段做视觉位移,不参与布局计算,父容器不感知子元素位置变化,动画效果也依赖子元素自身携带 .animation()alignItems 是布局阶段属性,直接决定子元素的排列起点,改变时布局重新计算,容器上的 .animation() 即可完整驱动过渡。



四、注意事项

  1. 真机运行:握姿感知依赖设备内置传感器,模拟器不会触发 holdingHandChanged 回调,需在支持该能力的 HarmonyOS 真机上验证。

  2. API 级别holdingHandChanged 事件及 HoldingHandStatus 枚举从 API 20 起支持,项目 compileSdkVersion6.1.0(23),满足要求。

  3. 设备能力检测:不支持握姿感知的设备,motion.on(...) 会抛出 BusinessError(801),代码中已在 catch 块捕获并通过 errorBanner 展示,不会白屏崩溃。

  4. 数据流方向:父页面用 @Local grip 持有 GripState 实例,子组件通过 @Param grip 接收引用,@Trace 字段变化会自动穿透刷新所有依赖该字段的子组件,无需额外 @Watch 或手动通知。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐