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手机设备均支持智感握姿

鸿蒙开发者班级链接:https://developer.huawei.com/consumer/cn/training/classDetail/c3109959e5ad4b5588581f414c885a97?type=1?ha_source=hmosclass&ha_sourceId=89000248

Logo

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

更多推荐