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'
@State accelX: number = 0
@State accelY: number = 0
@State accelZ: number = 0
private subscribed: boolean = false
subscribeAccelerometer() {
try {
sensor.on(sensor.SensorId.ACCELEROMETER, (data: sensor.AccelerometerResponse) => {
this.accelX = data.x
this.accelY = data.y
this.accelZ = data.z
}, { interval: 200000000 }) // 采样间隔,单位纳秒
this.subscribed = true
} catch (error) {
let e = error as BusinessError
console.error(`订阅失败: ${e.code}, ${e.message}`)
}
}
unsubscribeAccelerometer() {
try {
sensor.off(sensor.SensorId.ACCELEROMETER)
this.subscribed = false
} catch (error) {
let e = error as BusinessError
console.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
@Component
struct ShakePage {
@State shakeCount: number = 0
@State isShaking: boolean = false
private lastShakeTime: number = 0
private SHAKE_THRESHOLD: number = 18 // 加速度阈值
private SHAKE_INTERVAL: number = 500 // 两次摇动最小间隔(ms)
private ACCEL_INTERVAL: number = 50000000 // 50ms采样间隔
aboutToAppear() {
this.startShakeDetection()
}
aboutToDisappear() {
this.stopShakeDetection()
}
startShakeDetection() {
try {
sensor.on(sensor.SensorId.ACCELEROMETER, (data: sensor.AccelerometerResponse) => {
const acceleration = Math.abs(data.x) + Math.abs(data.y) + Math.abs(data.z)
const now = Date.now()
if (acceleration > this.SHAKE_THRESHOLD && !this.isShaking) {
if (now - this.lastShakeTime > this.SHAKE_INTERVAL) {
this.isShaking = true
this.lastShakeTime = now
this.onShakeDetected()
// 300ms后重置状态,避免一次摇动多次触发
setTimeout(() => {
this.isShaking = false
}, 300)
}
}
}, { interval: this.ACCEL_INTERVAL })
} catch (error) {
let e = error as BusinessError
console.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)
}
}
算法讲解
核心判断就这一行:
const acceleration = 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 那篇如果还没看,建议搭配着看,传感器 + 相机组合能做不少有趣的东西。
更多推荐



所有评论(0)