文章的目的为了记录使用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 …