?文章的目的為了記錄使用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)// 中間 CanvasCanvas(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 callbacksthis.onTimeChange = onTimeChange;// Start the time checking loopthis.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 changeif (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