本文原创发布在华为开发者社区。
介绍
本示例是主要分为3个部分:
- 蓝牙BLE连接
- 通过\@kit.ConnectivityKit实现蓝牙设备搜索和连接
- 体重、趋势图、柱状图、指标组件
- 通过canvas画布组件绘制体重仪表以及指标线,进行体重情况的可视化展示
- 通过mpchart实现趋势图以及柱状图的设计
- 传感器计步能力
- 初始化页面获取一次计步器传感器数据
- 获取完订阅计步检测器传感器数据
效果预览
使用说明
打开应用后,用户开启蓝牙授权,进入主页面,根据需求调用功能。(具体使用见readme)
实现思路
蓝牙BLE连接
- 工程主页请求蓝牙授权
- 页面打开时获取设备蓝牙开启状态
- 以serviceUuid为过滤条件进行扫描(见下代码),实际开发过程中可以按业务场景配置扫描条件或不设置,具体可以参考官网
- 基于扫描出来的随机Mac地址(deviceId)做连接/断开操作
- 可以基于connectStateChange获取连接状态做其他操作
let scanFilter: ble.ScanFilter = {
serviceUuid: "00001810-0000-1000-8000-00805F9B34FB"
};
let scanOptions: ble.ScanOptions = {
interval: 100,
dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER,
matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE,
}
ble.startBLEScan([scanFilter], scanOptions);
体重、趋势图、柱状图、指标组件
- 体重组件
- 调用体重组件,初始显示为零,在Button的OnClick事件中获取nowweight,在点击按钮后组件会将传入的值显示在仪表上,自动完成与上次体重差值的计算并显示
this.change = !this.change
this.oldWeight = this.nowWeight
this.change === false ? (this.weight = '100%') : (this.weight = (componentUtils.getRectangleById('flag2')
.size
.width
.toString() + 'px'))
//在这里传入测量的当前体重
//如果体重没有变化,不会触发左侧刷新,因此多加一条判断来刷新左侧
this.oldWeight === this.nowWeight ? (this.DV = 0) : (this.DV = this.nowWeight - this.oldWeight)
- 点击设置体重目标,可以在弹出的滑动窗口中选择数值,选中之后再次点击可以进行更改
TextPickerDialog.show({
//滑动弹窗选择器,封装函数更好
range: this.goals,
selected: this.select,
disappearTextStyle: { color: Color.Red, font: { size: 15, weight: FontWeight.Lighter } },
textStyle: { color: Color.Black, font: { size: 20, weight: FontWeight.Normal } },
selectedTextStyle: { color: Color.Blue, font: { size: 30, weight: FontWeight.Bolder } },
onAccept: (value: TextPickerResult) => {
// 设置select为按下确定按钮时候的选中项index,这样当弹窗再次弹出时显示选中的是上一次确定的选项
this.select = value.index
hilog.info(0x0000, "TextPickerDialog:onAccept()" + JSON.stringify(value), '')
this.flag = value.value.toString(); //刷新状态,更改体重目标为选中的数值
},
onCancel: () => {
hilog.info(0x0000, "TextPickerDialog:onCancel()", '')
},
onChange: (value: TextPickerResult) => {
hilog.info(0x0000, "TextPickerDialog:onChange()" + JSON.stringify(value), '')
}
})
- 组件中间的弧形进度条通过canvas画布组件实现,整个图形根据圆心的位置进行绘制,范围是0-150kg
Canvas(this.context)
.width(this.weight)
.height('100%')
.onReady(() => {...})
- 指标组件
- 组件由canvas画布绘制的五条线段组成,用不同颜色区分体重区间
//添加一个画布组件,利用其事件函数进行位置计算
Canvas(this.context)
.width('100%')
.height('100%')
.onReady(() => {
...
// 如果想要设置x轴偏移随体重变化而变化,可以自行计算x坐标,根据当前体重占当前区间的百分比以及屏幕和线段的长度就可以计算出来
if (this.weight >= WeightHiatus.START_HIATUS && this.weight <= WeightHiatus.FIRST_HIATUS) { //偏瘦图片,后续自行更换
let x_Offset = px2vp(componentUtils.getRectangleById('line1').localOffset.x) //计算每个线段相对于父组件的x偏移量
this.context.drawImage(Picture.img1, X_Positoin(x_Offset, WeightHiatus.START_HIATUS, WeightHiatus.FIRST_HIATUS, this.weight, lineWith), y_Position, SizeNumber.SIZE3, SizeNumber.SIZE3)
} else if (this.weight <= WeightHiatus.SECOND_HIATUS) {...}
else if (this.weight <= WeightHiatus.THIRD_HIATUS){...}
else if (this.weight <= WeightHiatus.FOURTH_HIATUS) {...} else {...}
})
- 趋势图、柱状图
- 组件通过调用三方库Mpchart实现,对x、y轴的数值形式重构以满足不同需求
传感器计步能力
- 获取一次计步器传感器数据
getPedometerData() {
try {
sensor.once(sensor.SensorId.PEDOMETER, (data: sensor.PedometerResponse) => {
hilog.info(0x0000, 'Succeeded in invoking once. Step count: ' + data.steps, '');
this.stepNum = data.steps ? data.steps : 0;
this.onPedometer()
});
} catch (error) {
let e: BusinessError = error as BusinessError;
hilog.error(0x0000, `Failed to invoke once. Code: ${e.code}, message: ${e.message}`, '');
}
}
- 订阅计步检测器传感器数据
onPedometer() {
try {
sensor.on(sensor.SensorId.PEDOMETER, (data: sensor.PedometerResponse) => {
hilog.info(0x0000, 'Succeeded in invoking on. Step count: ' + data.steps, '');
this.stepNum = data.steps ? data.steps : 0;
}, { interval: 100000000 });
} catch (error) {
let e: BusinessError = error as BusinessError;
hilog.error(0x0000, `Failed to invoke on. Code: ${e.code}, message: ${e.message}`, '');
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。