鸿蒙开发案例:直尺

news/2024/12/25 2:05:27/文章来源:https://www.cnblogs.com/zhongcx/p/18524160

【1】引言(完整代码在最后面)

本文将通过一个具体的案例——创建一个横屏显示的直尺应用,来引导读者了解鸿蒙应用开发的基本流程和技术要点。

【2】环境准备

电脑系统:windows 10

开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806

工程版本:API 12

真机:Mate 60 Pro

语言:ArkTS、ArkUI

【3】功能分析

1. 刻度线生成

生成直尺上的刻度线是直尺应用的基础。不同的刻度线有不同的高度,这有助于用户更准确地读取长度。

  for (let i = 0; i <= 15 * 10; i++) {let lineHeight: number = (i % 10 === 0) ? 90 : (i % 5 === 0) ? 60 : 45;this.rulerLines.push(new RulerLine(i, lineHeight));}

2. 刻度线编号显示

为了便于用户读取刻度,每隔一定数量的刻度线显示一个编号。这样可以减少视觉上的混乱,提高可读性。

class RulerLine {index: number;height: number;constructor(index: number, height: number) {this.index = index;this.height = height;}showNumber(): string {return this.index % 10 === 0 ? `${Math.floor(this.index / 10)}` : '';}
}

3. 屏幕方向设置

确保应用在横屏模式下显示,因为直尺更适合横向使用。

  window.getLastWindow(getContext()).then((windowClass) => {windowClass.setPreferredOrientation(window.Orientation.LANDSCAPE);});

4. 容器高度和宽度计算

动态计算容器的高度和宽度,以适应不同设备的屏幕尺寸。

onCellWidthChanged() {this.maxRulerHeight = vp2px(this.containerWidth) / this.cellWidthInPixels / 10;
}onContainerHeightChanged() {this.containerHeight = Math.max(this.containerHeight, 53);
}

5. 拖动手势处理

通过手势操作,用户可以更直观地调整直尺的位置和高度,提高用户体验。

Stack() {Circle({ height: 30, width: 30 }).fill("#019dfe").stroke(Color.Transparent).strokeWidth(3);Circle({ height: 40, width: 40 }).fill(Color.Transparent).stroke("#019dfe").strokeWidth(3);
}
.hitTestBehavior(HitTestMode.Block)
.padding(20)
.alignRules({center: { anchor: "__container__", align: VerticalAlign.Center },middle: { anchor: "__container__", align: HorizontalAlign.Start }
})
.gesture(PanGesture({fingers: 1,direction: PanDirection.Horizontal,distance: 1
}).onActionUpdate((event: GestureEvent) => {this.leftOffsetX = this.currentPositionX + event.offsetX / 2;this.containerHeight = this.originalContainerHeight - event.offsetX;
}).onActionEnd(() => {this.currentPositionX = this.leftOffsetX;this.originalContainerHeight = this.containerHeight;
}));Stack() {Circle({ height: 30, width: 30 }).fill("#019dfe").stroke(Color.Transparent).strokeWidth(3);Circle({ height: 40, width: 40 }).fill(Color.Transparent).stroke("#019dfe").strokeWidth(3);
}
.hitTestBehavior(HitTestMode.Block)
.padding(20)
.alignRules({center: { anchor: "__container__", align: VerticalAlign.Center },middle: { anchor: "__container__", align: HorizontalAlign.End }
})
.gesture(PanGesture({fingers: 1,direction: PanDirection.Horizontal,distance: 1
}).onActionUpdate((event: GestureEvent) => {this.leftOffsetX = this.currentPositionX + event.offsetX / 2;this.containerHeight = this.originalContainerHeight + event.offsetX;
}).onActionEnd(() => {this.currentPositionX = this.leftOffsetX;this.originalContainerHeight = this.containerHeight;
}));

6. 计数器调整

通过计数器,用户可以微调每毫米对应的像素值和选中区的距离,从而更精确地使用直尺。

Counter() {Text(`选中区距离:${this.maxRulerHeight.toFixed(2)}厘米`).fancy();
}
.foregroundColor(Color.White)
.width(300)
.onInc(() => {this.containerHeight = px2vp(vp2px(this.containerHeight) + this.cellWidthInPixels / 10);
})
.onDec(() => {this.containerHeight = px2vp(vp2px(this.containerHeight) - this.cellWidthInPixels / 10);
});Counter() {Text(`每毫米间距:${this.cellWidthInPixels.toFixed(2)}px`).fancy();
}
.foregroundColor(Color.White)
.width(300)
.onInc(() => {this.cellWidthInPixels += 0.01;
})
.onDec(() => {this.cellWidthInPixels = Math.max(0.01, this.cellWidthInPixels - 0.01);
});

7. 区域变化监听

当容器的区域发生变化时,需要及时更新容器的宽度,以确保直尺的显示正确。

RelativeContainer() {Rect().fill("#80019dfe").borderColor("#019dfe").borderWidth({ left: 1, right: 1 }).clip(true).width("100%").height("100%").onAreaChange((oldArea: Area, newArea: Area) => {this.containerWidth = newArea.width as number;});
}

【完整代码】

import { window } from '@kit.ArkUI'; // 导入窗口相关的API
import { deviceInfo } from '@kit.BasicServicesKit'; // 导入设备信息相关的API// 定义直尺线类
class RulerLine {index: number; // 线的索引height: number; // 线的高度constructor(index: number, height: number) {this.index = index; // 初始化索引this.height = height; // 初始化高度}// 显示线的编号showNumber(): string {return this.index % 10 === 0 ? `${Math.floor(this.index / 10)}` : ''; // 每10个线显示一个编号}
}// 扩展文本样式
@Extend(Text)
function fancy() {.fontColor("#019dfe") // 设置字体颜色.fontSize(20); // 设置字体大小
}// 定义直尺组件
@Entry
@Component
struct RulerComponent {@State maxRulerHeight: number = 0; // 最大直尺高度@State @Watch('onCellWidthChanged') cellWidthInPixels: number = 17.28; // 每毫米对应的像素@State textWidth: number = 80; // 文本宽度@State rulerLines: RulerLine[] = []; // 直尺线数组@State leftOffsetX: number = -300; // 左侧偏移@State currentPositionX: number = -300; // 当前X位置@State @Watch('onContainerHeightChanged') containerHeight: number = 53; // 容器高度@State originalContainerHeight: number = 53; // 原始容器高度@State @Watch('onCellWidthChanged') containerWidth: number = 0; // 容器宽度// 处理单元格宽度变化onCellWidthChanged() {this.maxRulerHeight = vp2px(this.containerWidth) / this.cellWidthInPixels / 10; // 更新最大直尺高度}// 处理容器高度变化onContainerHeightChanged() {this.containerHeight = Math.max(this.containerHeight, 53); // 确保容器高度不小于53}// 组件即将出现时aboutToAppear(): void {// 设置当前应用为横屏显示window.getLastWindow(getContext()).then((windowClass) => {windowClass.setPreferredOrientation(window.Orientation.LANDSCAPE); // 设置为横屏});// 初始化直尺线for (let i = 0; i <= 15 * 10; i++) {let lineHeight: number = (i % 10 === 0) ? 90 : (i % 5 === 0) ? 60 : 45; // 根据索引设置线的高度this.rulerLines.push(new RulerLine(i, lineHeight)); // 将新线添加到数组中}}// 构建UIbuild() {Column() { // 创建一个列布局Stack() { // 创建一个堆叠布局Stack() { // 创建另一个堆叠布局ForEach(this.rulerLines, (line: RulerLine, index: number) => { // 遍历直尺线数组Line()// 创建一条线.width(1)// 设置线宽.height(`${line.height}px`)// 设置线高.backgroundColor(Color.White)// 设置线的背景颜色.margin({ left: `${this.cellWidthInPixels * index}px` }); // 设置线的左边距Text(line.showNumber())// 显示线的编号.fontColor(Color.White)// 设置字体颜色.fontSize(18)// 设置字体大小.width(`${this.textWidth}px`)// 设置文本宽度.height(`${this.textWidth}px`)// 设置文本高度.textAlign(TextAlign.Center)// 设置文本对齐方式.margin({left: `${this.cellWidthInPixels * index - this.textWidth / 2}px`,top: `${line.height}px`}); // 设置文本位置});}.width('100%').height('100%').align(Alignment.TopStart); // 设置堆叠布局的宽高和对齐方式Column({ space: 15 }) { // 创建一个列布局,设置间距Text(`当前设备:${deviceInfo.marketName}`).fancy(); // 显示当前设备名称Counter() { // 创建一个计数器Text(`选中区距离:${this.maxRulerHeight.toFixed(2)}厘米`).fancy(); // 显示选中区距离}.foregroundColor(Color.White) // 设置计数器字体颜色.width(300) // 设置计数器宽度.onInc(() => { // 增加计数器时的处理this.containerHeight = px2vp(vp2px(this.containerHeight) + this.cellWidthInPixels / 10); // 更新容器高度}).onDec(() => { // 减少计数器时的处理this.containerHeight = px2vp(vp2px(this.containerHeight) - this.cellWidthInPixels / 10); // 更新容器高度});Counter() { // 创建另一个计数器Text(`每毫米间距:${this.cellWidthInPixels.toFixed(2)}px`).fancy(); // 显示每毫米间距}.foregroundColor(Color.White) // 设置计数器字体颜色.width(300) // 设置计数器宽度.onInc(() => { // 增加计数器时的处理this.cellWidthInPixels += 0.01; // 增加每毫米间距}).onDec(() => { // 减少计数器时的处理this.cellWidthInPixels = Math.max(0.01, this.cellWidthInPixels - 0.01); // 减少每毫米间距,确保不小于0.01});}RelativeContainer() { // 创建一个相对布局容器Rect()// 创建一个矩形.fill("#80019dfe")// 设置填充颜色.borderColor("#019dfe")// 设置边框颜色.borderWidth({ left: 1, right: 1 })// 设置边框宽度.clip(true)// 启用裁剪.width("100%")// 设置宽度为100%.height("100%")// 设置高度为100%.onAreaChange((oldArea: Area, newArea: Area) => { // 处理区域变化this.containerWidth = newArea.width as number; // 更新容器宽度});Stack() { // 创建一个堆叠布局Circle({ height: 30, width: 30 })// 创建一个圆形.fill("#019dfe")// 设置填充颜色.stroke(Color.Transparent)// 设置边框颜色为透明.strokeWidth(3); // 设置边框宽度Circle({ height: 40, width: 40 })// 创建另一个圆形.fill(Color.Transparent)// 设置填充颜色为透明.stroke("#019dfe")// 设置边框颜色.strokeWidth(3); // 设置边框宽度}.hitTestBehavior(HitTestMode.Block) // 设置碰撞检测行为.padding(20) // 设置内边距.alignRules({// 设置对齐规则center: { anchor: "__container__", align: VerticalAlign.Center }, // 垂直居中middle: { anchor: "__container__", align: HorizontalAlign.Start } // 左对齐}).gesture(PanGesture({// 左侧拖动手势fingers: 1, // 单指拖动direction: PanDirection.Horizontal, // 水平拖动distance: 1 // 最小拖动距离}).onActionUpdate((event: GestureEvent) => { // 拖动更新时的处理this.leftOffsetX = this.currentPositionX + event.offsetX / 2; // 更新左侧偏移this.containerHeight = this.originalContainerHeight - event.offsetX; // 更新容器高度}).onActionEnd(() => { // 拖动结束时的处理this.currentPositionX = this.leftOffsetX; // 更新位置this.originalContainerHeight = this.containerHeight; // 更新原始高度}));Stack() { // 创建另一个堆叠布局Circle({ height: 30, width: 30 })// 创建一个圆形.fill("#019dfe")// 设置填充颜色.stroke(Color.Transparent)// 设置边框颜色为透明.strokeWidth(3); // 设置边框宽度Circle({ height: 40, width: 40 })// 创建另一个圆.fill(Color.Transparent)// 设置填充颜色为透明.stroke("#019dfe")// 设置边框颜色.strokeWidth(3); // 设置边框宽度}.hitTestBehavior(HitTestMode.Block) // 设置碰撞检测行为.padding(20) // 设置内边距.alignRules({// 设置对齐规则center: { anchor: "__container__", align: VerticalAlign.Center }, // 垂直居中middle: { anchor: "__container__", align: HorizontalAlign.End } // 右对齐}).gesture(PanGesture({// 右侧拖动手势fingers: 1, // 单指拖动direction: PanDirection.Horizontal, // 水平拖动distance: 1 // 最小拖动距离}).onActionUpdate((event: GestureEvent) => { // 拖动更新时的处理this.leftOffsetX = this.currentPositionX + event.offsetX / 2; // 更新左侧偏移this.containerHeight = this.originalContainerHeight + event.offsetX; // 更新容器高度}).onActionEnd(() => { // 拖动结束时的处理this.currentPositionX = this.leftOffsetX; // 更新位置this.originalContainerHeight = this.containerHeight; // 更新原始高度}));}.width(this.containerHeight) // 设置宽度.height("100%") // 设置高度.translate({ x: this.leftOffsetX }) // 使用左侧偏移.gesture(PanGesture({// 左侧拖动手势fingers: 1, // 单指拖动direction: PanDirection.Horizontal, // 水平拖动distance: 1 // 最小拖动距离}).onActionUpdate((event: GestureEvent) => { // 拖动更新时的处理if (event) {this.leftOffsetX = this.currentPositionX + event.offsetX; // 更新左侧偏移}}).onActionEnd(() => { // 拖动结束时的处理this.currentPositionX = this.leftOffsetX; // 更新位置}));}}.height('100%').width('100%') // 设置高度和宽度.padding({ left: 30, right: 10 }) // 设置内边距.backgroundColor("#181b22"); // 设置背景颜色}
}

  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/826386.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024-三叶草安全技术小组第十五届极客大挑战 wp

Crypto 1.凯撒加密 YEI{CKRIUSK_ZU_2024_MKKQ_INGRRKTMK} 6位 SYC{WELCOME_TO_2024_GEEK_CHALLENGE} 2.RSA 原文: from Crypto.Util.number import bytes_to_long, getPrime from secret import flag p = getPrime(128) q = getPrime(128) n = p*q e = 65537 m = bytes_to_lon…

GPT 1-3 简单介绍

GPT-1 简介 2018年6月,OpenAI公司发表了论文"Improving Language Understanding by Generative Pretraining"(用生成式预训练提高模型的语言理解能力), 推出了具有 1.17亿个参数的GPT-1(Generative Pre-training,生成式预训练)模型。 GPT-1 采用 传统的语言模型方…

基于Java+SpringBoot+Mysql实现的快递柜寄取快递系统功能实现八

三、系统部分功能描述公告信息业务逻辑层Service、快递取出记录信息业务逻辑层Service、预约物品取出信息业务逻辑层Service、短信发送信息业务逻辑层Service、关于我们控制器Controller、后台用户信息控制器Controller、 快递员信息控制器Controller、物品类型控制器Controlle…

转存——Quartus II FPGA程序仿真运行时出现错误“error occurred during modelsim simulation”的解决方法

起因 使用Quartus II软件进行FPGA程序仿真,运行时出现错误“error occurred during modelsim simulation”,上网查询解决方法,找了很久都没找到,最后在一个CSDN博客的评论里找到解决方法。 现将解决方法转存如下。 错误示例解决步骤 1.依次点击simulation,option2.依次点击…

Android Studio启动安卓模拟器失败,出现The emulator process for AVD Medium_Phone_API_35 has terminated.

前言 软件版本已安装的SDK Tools包。Android Studio安装设置Proxy代理问题。可在此处设置代理,可在本窗口的左下角的Check Connection处进行检测链接的有效性。 也可以查看以下地址,设置代理的地址:阿里云Android仓库 清华大学开源软件镜像站模拟器问题如果你在这里运行安卓…

MyBatis-Spring中MyBatis概要流程

一、初始化SqlSessionFactory 核心流程 核心使用到了SqlSessionFactoryBean的afterPropertiesSet、getObject方法 afterPropertiesSet:用于初始化并封装数据 getObject:用于注入DefaultSqlSessionFactory对象到容器中 详情逻辑 一、在将SqlSessionFactoryBean放在IOC容器过程…

DP杂题专练

前言 DP 方面太菜了,要多练习,多思考,多做好题。 摘花生Hello Kitty想摘点花生送给她喜欢的米老鼠。 她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。 地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上…

基于Axure,对抖音直播,淘宝直播和b站直播的对比分析

设计思路 一、竞品分析 1.竞品选择 淘宝直播,抖音直播,b站直播 2.产品定位对比 2.1搜索指数:从2024年年初到2024年10月底,不难看出抖音直播相较于淘宝和b站直播来说,搜索指数更加的高昂,所带来的流量数据也更多。 抖音直播(绿色线)整体搜索量最高,日均值为2,694,且在…

基于Java+SpringBoot+Mysql实现的快递柜寄取快递系统功能实现七

二、主要技术: 2.1 SpringBoot技术SpringBoot是基于Spring框架的一个开发框架,旨在简化Spring应程序的搭建和开发过程以下是关于SpringBoot技术的一些主要特点和优势: SpringBoot提供了许多自动配置的功能,可以根据的依赖和需要自动配置应程序所需的环境,大大少了开发人员…

学期2024-2025-1 学号20241306 《计算机基础与程序设计》第6周学习总结

学期(如2024-2025-1) 学号(如:20241300) 《计算机基础与程序设计》第X周学习总结 作业信息这个作业属于哪个课程 2024-2025-1-计算机基础与程序设计(https://edu.cnblogs.com/campus/besti/2024-2025-1-CFAP))这个作业要求在哪里 [2024-2025-1计算机基础与程序设计第6周作…

九州信泰杯 第十一届山东省网络安全技能大赛

九州信泰杯 第十一届山东省网络安全技能大赛MISC1.签到这是真签到,下载后即可得到flag2.ezpic打开后,这是一个给了一张图片然后拖到010里面,在末尾找到了一半flag另一半打开打stegsloves通过改变背景颜色然后在里面找到了一个二维码扫码后得到flag的另一半最后拼接flag{cf74…

2024数模b题-问题一思路构建

2024数模b题-问题一思路构建样本量计算根据置信空间的计算公式,逆累积分布函数(ICDF):逆累积分布函数(ICDF)是从累积分布函数的值反推出对应的Z分数。在MATLAB中,norminv 函数就是计算标准正态分布的逆累积分布函数的值我们通过这个公式来得到我们可以得到对于95%置信水平…