文章的目的为了记录使用Arkts 进行Harmony app 开发学习的经历。本职为嵌入式软件开发,公司安排开发app,临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。
相关链接:
开源 Arkts 鸿蒙应用 开发(一)工程文件分析-CSDN博客
开源 Arkts 鸿蒙应用 开发(二)封装库.har制作和应用-CSDN博客
开源 Arkts 鸿蒙应用 开发(三)Arkts的介绍-CSDN博客
开源 Arkts 鸿蒙应用 开发(四)布局和常用控件-CSDN博客
开源 Arkts 鸿蒙应用 开发(五)控件组成和复杂控件-CSDN博客
开源 Arkts 鸿蒙应用 开发(六)数据持久--文件和首选项存储-CSDN博客
开源 Arkts 鸿蒙应用 开发(七)数据持久--sqlite关系数据库-CSDN博客
开源 Arkts 鸿蒙应用 开发(八)多媒体--相册和相机-CSDN博客
开源 Arkts 鸿蒙应用 开发(九)通讯--tcp客户端-CSDN博客
开源 Arkts 鸿蒙应用 开发(十)通讯--Http-CSDN博客
开源 Arkts 鸿蒙应用 开发(十一)证书和包名修改-CSDN博客
开源 Arkts 鸿蒙应用 开发(十二)传感器的使用-CSDN博客
开源 Arkts 鸿蒙应用 开发(十三)音频--MP3播放_arkts avplayer播放音频 mp3-CSDN博客
开源 Arkts 鸿蒙应用 开发(十四)线程--任务池(taskpool)-CSDN博客
开源 Arkts 鸿蒙应用 开发(十五)自定义绘图控件--仪表盘-CSDN博客
开源 Arkts 鸿蒙应用 开发(十六)自定义绘图控件--波形图-CSDN博客
开源 Arkts 鸿蒙应用 开发(十七)通讯--http多文件下载-CSDN博客
开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器-CSDN博客
推荐链接:
开源 java android app 开发(一)开发环境的搭建-CSDN博客
开源 java android app 开发(二)工程文件结构-CSDN博客
开源 java android app 开发(三)GUI界面布局和常用组件-CSDN博客
开源 java android app 开发(四)GUI界面重要组件-CSDN博客
开源 java android app 开发(五)文件和数据库存储-CSDN博客
开源 java android app 开发(六)多媒体使用-CSDN博客
开源 java android app 开发(七)通讯之Tcp和Http-CSDN博客
开源 java android app 开发(八)通讯之Mqtt和Ble-CSDN博客
开源 java android app 开发(九)后台之线程和服务-CSDN博客
开源 java android app 开发(十)广播机制-CSDN博客
开源 java android app 开发(十一)调试、发布-CSDN博客
开源 java android app 开发(十二)封库.aar-CSDN博客
推荐链接:
开源C# .net mvc 开发(一)WEB搭建_c#部署web程序-CSDN博客
开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-CSDN博客
开源 C# .net mvc 开发(三)WEB内外网访问(VS发布、IIS配置网站、花生壳外网穿刺访问)_c# mvc 域名下不可訪問內網,內網下可以訪問域名-CSDN博客
开源 C# .net mvc 开发(四)工程结构、页面提交以及显示_c#工程结构-CSDN博客
开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-CSDN博客
本章内容主要演示了如何使自定义控件,通过画布实现一个模拟车速表的应用。
1.工程结构
2.源码解析
3.图片资源
4.演示效果
5.工程下载网址
一、工程结构如下图,需要注意的是除了CanvasCom.ets文件为自定义控件以外,还需要用到2张图片,使用analog_clock_bg.png作为表盘背景,使用analog_clock_second_hand.png作为指针,可以很方便的自行修改表盘和指针。

二、源码解析
2.1 index.ets
这是主页面组件,主要功能是:
提供一个加速按钮来控制速度,显示当前速度,嵌入CanvasCom组件来显示车速表
关键点:
使用@State装饰器管理当前速度(currentSpeed)和整型速度(intspeed)
点击"加速"按钮时,速度增加10,超过240后归零
将速度传递给CanvasCom组件作为属性
以下为代码
import { CanvasCom } from './CanvasCom'
import { router } from '@kit.ArkUI'
const btnStep = 10;
@Entry
@Component
struct Index {
@State currentSpeed: string = '0' // 添加状态管理speed
@State intspeed:number = 0;
build() {
Column() {
// 速度控制按钮组
Row() {
Button('加速')
.onClick(() => {
this.intspeed = this.intspeed + btnStep;
if(this.intspeed>240)this.intspeed=0;
this.currentSpeed = this.intspeed.toString();
})
.margin(10)
Text(`当前速度: ${this.currentSpeed}`)
.fontSize(20)
.margin(10)
}
.margin({ top: 20 })
// 或者直接嵌入使用
CanvasCom({
desc: '车速表',
title: '时钟示例',
speed: this.currentSpeed
})
}
.width('100%')
.height('100%')
}
}
2.2 CanvasCom.ets
这是核心组件,实现了一个模拟车速表的功能,主要特点:
组件结构
接收desc、title和speed作为输入,使用@Watch装饰器监听_speed变化,触发表针移动,使用Canvas绘制车速表界面
表盘绘制:使用analog_clock_bg.png作为表盘背景,使用analog_clock_second_hand.png作为指针
速度映射:将速度值(0-240)映射到表盘角度(225°-45°),使用watchStart(225)和watchProp(180/160=1.125)进行线性映射
动画效果:
使用TimeChangeListener每200ms刷新一次界面
在timeChanged()方法中根据当前速度重绘指针位置
以下为代码
import { router } from '@kit.ArkUI';
import { TimeChangeListener } from './TimeChangeListener';
import { BusinessError } from '@kit.BasicServicesKit';
import image from '@ohos.multimedia.image';
// 常量定义
const ANGLE_PRE_SECOND = 1;
const CANVAS_SIZE = 250;
const CANVAS_ASPACTRADIO = 1;
const IMAGE_WIDTH = 10*3;
// 时钟图片名称
const CLOCK_BG_PATH = 'analog_clock_bg.png';
const CLOCK_SECOND_PATH = 'analog_clock_second_hand.png';
const watchStart = 225;
const watchProp = 180/(200-40);
@Entry
@Component
export struct CanvasCom {
@State desc: string = '';
@State title: string = '';
@Prop speed:string ='0';
// 2. 内部真正驱动绘制的状态
@State @Watch('onSpeedChange') _speed: number =0;
// 主要代码区域
@State time: string = '';
private settings: RenderingContextSettings = new RenderingContextSettings(true);
private renderContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
private canvasSize: number = CANVAS_SIZE;
private clockRadius: number = this.canvasSize / 2;
private resourceDir: string = getContext(this).resourceDir;
private clockPixelMap: image.PixelMap | null = null;
private secondPixelMap: image.PixelMap | null = null;
private timeListener: TimeChangeListener | null = null;
onSpeedChange() {
// 把 speed 映射到秒针角度
//const second = this._speed ;
const second = watchStart+ this._speed * watchProp;
this.timeChanged(second);
}
onPageShow(): void {
const params = router.getParams() as Record<string, string>;
if (params) {
this.desc = params.desc as string;
this.title = params.value as string;
// 移除 this.speed = params.value as string
}
}
aboutToAppear(): void {
//this._speed = Number(this.speed) || 245;
this.init();
}
aboutToDisappear(): void {
if (this.timeListener) {
this.timeListener.clearInterval();
}
}
// 生命周期:父组件传值变化
aboutToUpdate(): void {
}
build() {
Column() {
// 顶部描述文本
Text(this.desc)
.fontSize(30)
.fontWeight(FontWeight.Bold)
.margin(20)
// 中间 Canvas
Canvas(this.renderContext)
.width(this.canvasSize)
.aspectRatio(CANVAS_ASPACTRADIO)
.onReady(() => {
this.paintTask();
})
// 底部时间文本
Text(`当前速度: ${this.speed}`)
.fontSize(20)
.margin(20)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
/**
* 初始化表盘和表针对应的变量,并首次绘制。
*/
private init() {
const clockBgSource = image.createImageSource(this.resourceDir + '/' + CLOCK_BG_PATH);
const secondSource = image.createImageSource(this.resourceDir + '/' + CLOCK_SECOND_PATH);
// 创建表盘对应的PixelMap并绘制。
let paintDial = clockBgSource.createPixelMap().then((pixelMap: image.PixelMap) => {
this.clockPixelMap = pixelMap;
this.paintDial();
}).catch((err: BusinessError) => {
console.log('打印错误信息')
});
// 创建秒针对应的PixelMap并绘制。
secondSource.createPixelMap().then(async (pixelMap: image.PixelMap) => {
await paintDial;
//this.paintPin(ANGLE_PRE_SECOND * 10, pixelMap);
this.paintPin(ANGLE_PRE_SECOND * watchStart, pixelMap);
this.secondPixelMap = pixelMap;
}).catch((err: BusinessError) => {
console.log('打印错误信息')
});
}
/**
* 绘制模拟时钟任务
*/
private paintTask() {
// 1.先将绘制原点转到画布中央
this.renderContext.translate(this.clockRadius, this.clockRadius);
// 2.监听时间变化,每秒重新绘制一次
this.timeListener = new TimeChangeListener(
(hour: number, minute: number, second: number) => {
this.renderContext.clearRect(-this.clockRadius, -this.clockRadius, this.canvasSize, this.canvasSize);
this.paintDial();
//this.timeChanged(15);
//const initSecond = Number(this.speed) || 0;
const initSecond = watchStart + watchProp * Number(this.speed);
//this.timeChanged(initSecond % 60);
this.timeChanged(initSecond );
},
);
}
/**
* 时间变化回调函数
*/
private timeChanged(newSecond: number) {
this.paintPin(ANGLE_PRE_SECOND * newSecond, this.secondPixelMap);
}
/**
* 绘制表盘
*/
private paintDial() {
this.renderContext.beginPath();
if (this.clockPixelMap) {
this.renderContext.drawImage(
this.clockPixelMap,
-this.clockRadius,
-this.clockRadius,
this.canvasSize,
this.canvasSize)
} else {
console.log('打印错误信息')
}
}
/**
* 绘制表针
*/
private paintPin(degree: number, pinImgRes: image.PixelMap | null) {
this.renderContext.save();
const angleToRadian = Math.PI / 180;
let theta = degree * angleToRadian;
this.renderContext.rotate(theta);
this.renderContext.beginPath();
if (pinImgRes) {
this.renderContext.drawImage(
pinImgRes,
-IMAGE_WIDTH / 2,
-this.clockRadius /2,
IMAGE_WIDTH,
this.canvasSize / 2 );
} else {
console.log('打印错误信息')
}
this.renderContext.restore();
}
}
2.3 TimeChangeListener.ets
这是一个辅助类,用于定期触发回调:每200ms获取当前时间并回调,提供清理定时器的方法
以下为代码
// 回调声明
type TimeChangeCallback = (hour: number, minute: number, second: number,) => void;
// 时钟刷新间隔
const REFRESH_INTERVAL = 200;
export class TimeChangeListener {
private onTimeChange: TimeChangeCallback;
private intervalId: number = 0;
constructor(
onTimeChange: TimeChangeCallback
) {
// Store the callbacks
this.onTimeChange = onTimeChange;
// Start the time checking loop
this.intervalId = setInterval(() => this.checkTime(), REFRESH_INTERVAL); // Check every second
}
private checkTime(): void {
const now = new Date();
const currentHour = now.getHours();
const currentMinute = now.getMinutes();
const currentSecond = now.getSeconds();
// Check for second change
if (this.onTimeChange) {
this.onTimeChange(currentHour, currentMinute, currentSecond);
}
}
public clearInterval():void {
clearInterval(this.intervalId);
}
}
三、图片资源
analog_clock_bg.png

analog_clock_second_hand.png

四、演示效果

五、工程下载网址:https://download.csdn.net/download/ajassi2000/91681188