開源 Arkts 鴻蒙應用 開發(十五)自定義繪圖控件--儀表盤

?文章的目的為了記錄使用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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/93422.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93422.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93422.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

???????中國工業企業專利及引用被引用數據說明

1319 中國工業企業專利及引用被引用數據說明數據簡介專利近年發文趨勢及主題分布今天數據皮皮俠團隊為大家分享一份2023年12月25日最新更新的中國工業企業專利及引用被引用數據&#xff0c;供大家研究使用。數據來源原始數據來源于國家統計局&#xff0c;由皮皮俠團隊整理計算。…

MySQL知識點(上)

MySQL知識點 一&#xff1a;MySQL概述 MySQL是一款開源的數據庫軟件&#xff0c;是一種關系型數據庫管理系統&#xff08;ROBMS&#xff09;&#xff0c;也叫做表數據庫管理系統 如果需要快速安全地處理大量的數據&#xff0c;則必須使用數據庫管理系統&#xff1b;任何基于數據…

shell腳本實現sha256sum校驗并拷貝校驗通過的文件

#!/bin/bash# 目標目錄 TARGET_DIR"/appdata/jn1m/versions/old/bin"# 校驗文件 CHECKSUM_FILE"checksum.txt"# 檢查目標目錄是否存在 if [ ! -d "$TARGET_DIR" ]; thenecho "錯誤&#xff1a;目標目錄 $TARGET_DIR 不存在"exit 1 fi#…

中小型泵站物聯網智能控制系統解決方案:構建無人值守的自動化泵站體系

一、系統核心架構與功能設計1.物聯網感知層設備互聯&#xff1a;網關對接壓力傳感器、超聲波液位計、智能電表、振動傳感器等&#xff0c;實時采集水泵運行狀態&#xff08;流量、壓力、溫度、振動&#xff09;、液位、水質&#xff08;pH值、濁度&#xff09;、能耗等關鍵參數…

網絡通信---Axios

1、什么是 Axios&#xff1f; Axios? 是一個基于 ?Promise? 的 ?HTTP 客戶端&#xff0c;用于瀏覽器和 Node.js 環境&#xff0c;用來發送 ?HTTP 請求&#xff08;如 GET、POST、PUT、DELETE 等&#xff09;?。 它常用于&#xff1a; 向后臺 API 發送請求獲取數據提交表…

Ubuntu 軟件源版本不匹配導致的依賴沖突問題及解決方法

在使用 Ubuntu 系統的過程中&#xff0c;軟件包管理是日常操作的重要部分。但有時我們會遇到各種依賴沖突問題&#xff0c;其中軟件源與系統版本不匹配是常見且棘手的一種。本文就來詳細分享一次因軟件源版本不匹配引發的依賴沖突問題&#xff0c;以及具體的解決思路和流程。一…

思考:高速場景的行星輪混動效率如何理解

行星輪混動 E-CVT&#xff08;電子無級變速器&#xff09;是一種專為混合動力汽車設計的動力分配系統&#xff0c;其核心原理是通過行星齒輪組和電機的協同工作&#xff0c;實現動力分流與無級變速。 一、核心結構與組成 E-CVT的核心部件包括 行星齒輪組 和 雙電機&#xff08;…

跨域及解決方案

跨域&#xff08;Cross-Origin&#xff09;是指瀏覽器在執行 JavaScript 的時候&#xff0c;因為同源策略&#xff08;Same-Origin Policy&#xff09;的限制&#xff0c;阻止了一個網頁去請求不同源&#xff08;域名、端口、協議有任意一個不同&#xff09;的資源。 1. 什么是…

PCA降維全解析:從原理到實戰

一文讀懂PCA降維&#xff1a;原理、實現與可視化全解析?本文6000字&#xff0c;涵蓋PCA核心原理、數學推導、代碼實戰及高頻面試題&#xff0c;建議收藏閱讀?一、為什么需要降維&#xff1f;數據爆炸時代的生存法則當數據集的特征維度激增&#xff08;如基因數據、推薦系統用…

Kafka工作機制深度解析:Broker、Partition 與消費者組協作原理

&#x1f42f; Kafka工作機制深度解析&#xff1a;Broker、Partition 與消費者組協作原理 &#x1f3c1; 前言 Kafka 已成為互聯網公司流式數據處理的事實標準&#xff0c;廣泛應用于日志收集、實時計算、事件驅動架構等場景。 很多開發者會用 Kafka&#xff0c;但不了解它底…

深入解析live555:開源流媒體框架的技術原理與應用實踐

引言&#xff1a;流媒體領域的"老兵"與技術基石 在實時音視頻傳輸技術的發展歷程中&#xff0c;live555作為一款誕生于1990年代末的開源項目&#xff0c;至今仍在流媒體服務器、嵌入式設備和安防監控等領域發揮著不可替代的作用。它由Live Networks公司開發并維護&a…

EN55014家用電器、電動工具和類似設備的電磁兼容

一、EN 55014標準定義與屬性&#xff1f;EN 55014 是針對家用電器、電動工具及類似設備的電磁兼容&#xff08;EMC&#xff09;標準&#xff0c;主要規定了這類產品在電磁騷擾發射&#xff08;避免干擾其他設備&#xff09;和抗擾度&#xff08;抵抗其他設備干擾&#xff09;方…

python自學筆記9 Seaborn可視化

Seaborn&#xff1a;統計可視化利器 作為基于 Matplotlib 的高級繪圖庫&#xff0c;有一下功能&#xff1a;一元特征數據 直方圖 import matplotlib.pyplot as plt import pandas as pd import seaborn as sns # import os # # 如果文件夾不存在&#xff0c;創建文件夾 # if…

kafka 消費者組的概念是什么?它是如何實現消息的點對點和發布/訂閱模式?

Kafka 消費者組&#xff08;Consumer Group&#xff09;是 Kafka 架構中的核心概念&#xff0c;它是一組共同協作來消費一個或多個主題&#xff08;Topic&#xff09;數據的消費者應用的集合。 通過簡單地為多個消費者實例配置相同的 group.id&#xff0c;它們就組成了一個消費…

C#文件復制異常深度剖析:解決“未能找到文件“之謎

一個看似簡單的文件操作問題 在C#開發中&#xff0c;文件操作是基礎中的基礎&#xff0c;但有時最基礎的File.Copy()方法也會拋出令人困惑的異常。最近我遇到了這樣一個問題&#xff1a; File.Copy(sourceFile, targetFilePath);當targetFilePath設置為D:\25Q1\MR3.6.6.1_C1.2.…

OpenCV Python——圖像查找(特征匹配 + 單應性矩陣)

1 圖像查找&#xff08;單應性矩陣&#xff09;2 單應性矩陣 應用舉例3 單應性矩陣 代碼示例P87 111 圖像查找&#xff08;單應性矩陣&#xff09; 特征匹配作為輸入&#xff0c;獲得單應性矩陣 點X在img1和img2中的成像分別為x,x 圖中H即為單應性矩陣 2 單應性矩陣 應用…

Ubuntu 安裝帶證書的 etcd 集群

1.概念 etcd 是由GO語言編寫的分布式的、可靠的鍵值存儲系統&#xff0c;主要用于分布式系統中關鍵數據的存儲和服務發現。 2.核心概念 節點&#xff08;Node&#xff09; 每個運行 etcd 的實例被稱為一個節點。一個或多個節點可以組成一個集群。 集群&#xff08;Cluster&…

360 集團20周年會:戰略升級ALL IN Agent,搶占智能體時代先機

發布 | 大力財經8月15日&#xff0c;360集團迎來二十周年&#xff0c;在北京奧林匹克體育中心舉辦的“360集團20周年榮耀慶典”上&#xff0c;創始人周鴻祎向現場數千名員工發表演講&#xff0c;回顧360集團二十年的發展歷程&#xff0c;并明確360集團下一階段的公司戰略&#…

命令模式C++

命令模式&#xff08;Command Pattern&#xff09;是一種行為型設計模式&#xff0c;它將請求封裝為一個對象&#xff0c;使你可以用不同的請求對客戶進行參數化&#xff0c;還能支持請求的排隊、記錄日志及撤銷操作。這種模式將發送者和接收者解耦&#xff0c;發送者無需知道接…

Web攻防-大模型應用LLM搭建接入第三方內容喂養AI插件安全WiKI庫技術賦能

知識點&#xff1a; 1、WEB攻防-LLM搭建-AI喂養&安全知識WIKI庫 演示案例&#xff1a;WEB攻防-LLM搭建-AI喂養&安全知識WIKI庫 使用參考 https://docs.web2gpt.ai/ https://mp.weixin.qq.com/s/qqTOW5Kg1v0uxdSpbfriaA 0、服務器環境&#xff1a;阿里云 Ubuntu22.04 …