HarmonyOS7 传感器开发:加速度计做个摇一摇,原来这么简单
文章目录
- 前言
- 传感器能做什么
- 权限声明
- 加速度计接入
- 摇一摇实现
- 完整代码
- 算法讲解
- 采样率与功耗
- 其他传感器速览
- 写在最后
前言
摇一摇,这个功能最早是微信搞出来的,结果成了移动端的经典交互。在 HarmonyOS7 上实现摇一摇,比我想象的简单多了——加速度计数据就三个值,判断逻辑十几行代码,回调里加个震动就完事。但传感器开发有不少细节:权限怎么申、采样率怎么选、功耗怎么控。这篇从加速度计接入到摇一摇完整实现,顺便把其他传感器也扫一遍。
传感器能做什么
HarmonyOS7 支持的传感器不少,常见的这些:
| 传感器 | 数据 | 能干啥 |
|---|---|---|
| 加速度计 | x/y/z 三轴加速度 | 摇一摇、计步、屏幕旋转 |
| 陀螺仪 | x/y/z 三轴角速度 | 姿态检测、VR 头控 |
| 磁力计 | x/y/z 三轴磁场 | 指南针、金属检测 |
| 接近光 | 距离值 | 通话时熄屏 |
| 环境光 | 光照强度 | 自动亮度 |
| 气压计 | 大气压值 | 海拔估算、天气 |
| 计步器 | 步数 | 运动统计 |
| 心率 | BPM | 健康监测 |
每个传感器的接入方式都一样:on订阅 → 回调拿数据 →off取消订阅。套路统一,学会一个就会所有。
权限声明
加速度计需要权限,module.json5里加上:
{"requestPermissions":[{"name":"ohos.permission.ACCELEROMETER"}]}这个权限不需要动态申请,声明了就行。但心率传感器那类敏感数据需要
ohos.permission.HEALTH_DATA,那个要动态授权。
加速度计接入
核心就三步:导入模块、订阅数据、取消订阅。
import{sensor}from'@kit.SensorServiceKit'import{BusinessError}from'@kit.BasicServicesKit'@StateaccelX:number=0@StateaccelY:number=0@StateaccelZ:number=0privatesubscribed:boolean=falsesubscribeAccelerometer(){try{sensor.on(sensor.SensorId.ACCELEROMETER,(data:sensor.AccelerometerResponse)=>{this.accelX=data.xthis.accelY=data.ythis.accelZ=data.z},{interval:200000000})// 采样间隔,单位纳秒this.subscribed=true}catch(error){lete=errorasBusinessErrorconsole.error(`订阅失败:${e.code},${e.message}`)}}unsubscribeAccelerometer(){try{sensor.off(sensor.SensorId.ACCELEROMETER)this.subscribed=false}catch(error){lete=errorasBusinessErrorconsole.error(`取消订阅失败:${e.code}`)}}关键讲解:
sensor.SensorId.ACCELEROMETER:传感器类型标识,加速度计固定用这个data.x / data.y / data.z:三轴加速度值,单位 m/s²。手机静止平放时,z 轴约等于 9.8(重力),x/y 接近 0{ interval: 200000000 }:采样间隔,单位是纳秒。200000000ns = 200ms,也就是每秒采样 5 次。这个值后面详细讲on和off必须成对出现,页面销毁时一定记得 off,不然后台一直吃电
摇一摇实现
加速度计接好了,摇一摇的判断逻辑很直接——三轴加速度的绝对值之和超过阈值,就是"摇了"。
完整代码
import{sensor}from'@kit.SensorServiceKit'import{vibration}from'@kit.VibrationEffectKit'import{BusinessError}from'@kit.BasicServicesKit'@Entry@Componentstruct ShakePage{@StateshakeCount:number=0@StateisShaking:boolean=falseprivatelastShakeTime:number=0privateSHAKE_THRESHOLD:number=18// 加速度阈值privateSHAKE_INTERVAL:number=500// 两次摇动最小间隔(ms)privateACCEL_INTERVAL:number=50000000// 50ms采样间隔aboutToAppear(){this.startShakeDetection()}aboutToDisappear(){this.stopShakeDetection()}startShakeDetection(){try{sensor.on(sensor.SensorId.ACCELEROMETER,(data:sensor.AccelerometerResponse)=>{constacceleration=Math.abs(data.x)+Math.abs(data.y)+Math.abs(data.z)constnow=Date.now()if(acceleration>this.SHAKE_THRESHOLD&&!this.isShaking){if(now-this.lastShakeTime>this.SHAKE_INTERVAL){this.isShaking=truethis.lastShakeTime=nowthis.onShakeDetected()// 300ms后重置状态,避免一次摇动多次触发setTimeout(()=>{this.isShaking=false},300)}}},{interval:this.ACCEL_INTERVAL})}catch(error){lete=errorasBusinessErrorconsole.error(`启动检测失败:${e.code}`)}}stopShakeDetection(){try{sensor.off(sensor.SensorId.ACCELEROMETER)}catch(error){// ignore}}onShakeDetected(){this.shakeCount++// 震动反馈try{vibration.vibrate(vibration.VibrateType.VIBRATOR_LIGHT)}catch(e){// 没有震动模块就算了}}build(){Column({space:20}){Text('摇一摇计数器').fontSize(28).fontWeight(FontWeight.Bold)Text(`${this.shakeCount}`).fontSize(72).fontColor('#FF6D00').fontWeight(FontWeight.Bold)Button('重置').onClick(()=>{this.shakeCount=0})Text('用力摇晃手机试试').fontSize(14).fontColor('#999999')}.width('100%').height('100%').justifyContent(FlexAlign.Center)}}算法讲解
核心判断就这一行:
constacceleration=Math.abs(data.x)+Math.abs(data.y)+Math.abs(data.z)三轴加速度取绝对值求和。静止时这个值约 9.8(纯重力),使劲摇的时候能到 25+。阈值设 18是个经验值——正常走路大概 12-15,不会误触;稍微用点力摇就能到 18。
两个防抖机制缺一不可:
SHAKE_INTERVAL = 500ms:两次触发至少间隔 500ms,防止快速连续触发isShaking状态锁 +setTimeout(300ms):一次摇动只算一次,300ms 内不再响应
没有防抖的话,一次摇动会触发 5-10 次回调(因为采样间隔 50ms),计数器直接飞。
采样率与功耗
采样间隔怎么选?这事儿得权衡:
| 间隔 | 采样率 | 精度 | 功耗 | 适用场景 |
|---|---|---|---|---|
| 10000000ns (10ms) | 100Hz | 高 | 高 | 游戏、VR |
| 50000000ns (50ms) | 20Hz | 中 | 中 | 摇一摇、手势识别 |
| 200000000ns (200ms) | 5Hz | 低 | 低 | 屏幕旋转、基础检测 |
| 1000000000ns (1s) | 1Hz | 很低 | 很低 | 后台监控 |
摇一摇用 50ms(20Hz)刚刚好——采样够密不会漏摇,功耗也不会炸。如果你做的是计步器那种长时间运行的,建议 200ms 甚至更低,不然电池扛不住。
真心建议:不用的时候一定 off。我见过一个 App 后台跑着传感器没关,4 小时掉电 20%。用户骂的你听不见。
其他传感器速览
加速度计学会了,其他传感器套路一模一样,换下SensorId就行:
- 陀螺仪:
sensor.SensorId.GYROSCOPE,回调里data.x/y/z是角速度 - 磁力计:
sensor.SensorId.MAGNETIC_FIELD,做指南针配合GeomagneticField算偏角 - 接近光:
sensor.SensorId.PROXIMITY,通话时检测脸靠近就熄屏,data.distance - 环境光:
sensor.SensorId.AMBIENT_LIGHT,data.intensity是勒克斯值 - 计步器:
sensor.SensorId.PEDOMETER,data.steps直接给你步数,不用自己算 - 心率:
sensor.SensorId.HEART_RATE,需要额外权限,穿戴设备才支持
每种传感器的on/off用法完全一致,唯一区别是回调数据的字段名不同。官方文档里有每个传感器的 Response 类型定义,查一下就行。
写在最后
传感器开发真没那么难——on订阅、回调拿数据、off释放,三步走天下。摇一摇的实现核心就是加速度阈值判断 + 防抖,十几行代码搞定。但功耗控制和资源释放这些细节才是区分"能用"和"好用"的关键——不用就关、采样率别贪高、页面销毁一定 off。
下次如果你做计步器、指南针或者体感游戏,回来翻这篇就行,套路都一样。Camera 那篇如果还没看,建议搭配着看,传感器 + 相机组合能做不少有趣的东西。
