发布时间:2026/8/26 3:18:20
# 简易画板深度解析:Grid 像素格布局、画图/擦除模式切换与颜色预设 一、引言画板是用户创意表达的工具。虽然 ArkTS 原生支持Canvas组件用于自由绘图但本应用采用了一种更轻量、更声明式的方式——使用Grid网格布局模拟 10×10 像素画板每个格子是一个独立的GridItem。从技术角度来看这个应用覆盖了 ArkTS 中几个独特的模式Grid 的精确网格控制columnsTemplate(1fr × 10)创建 10×10 的规整像素网格状态驱动的像素着色每个格子的颜色由数组状态管理点击即改变模式切换画图/擦除通过Button的选中态装饰器切换两种交互模式颜色预设选择7 种预设颜色选中态高亮显示模拟像素艺术在无 Canvas 的情况下实现类像素画的交互体验本文将从网格布局的数学原理出发深入到 ArkTS 中用数组驱动 UI 渲染的核心思想。二、完整源码// index16.etsEntryComponentstruct Index16{Stategrid:number[]newArray(100).fill(0);StatecurrentColor:string#007AFF;StateisEraseMode:booleanfalse;privatecolors:string[][#FF3B30,#FF9500,#FFCC00,#34C759,#007AFF,#AF52DE,#000000];paintCell(index:number):void{this.grid[index]this.isEraseMode?0:1;}clearAll():void{this.gridnewArray(100).fill(0);}getCellColor(value:number):string{returnvalue1?this.currentColor:#F0F0F0;}build(){Column(){Text( 简易画板).fontSize(28).fontWeight(FontWeight.Bold).margin({top:40,bottom:5})Text(点击格子涂色10×10 像素画板).fontSize(13).fontColor(#8E8E93).margin({bottom:15})// 颜色预设Row({space:6}){ForEach(this.colors,(color:string){Column().width(28).height(28).backgroundColor(color).borderRadius(14).border({width:this.currentColorcolor!this.isEraseMode?3:1,color:this.currentColorcolor!this.isEraseMode?#333333:#DDDDDD}).onClick((){this.currentColorcolor;this.isEraseModefalse;})},(color:string)color)}.margin({bottom:10})// 模式切换Row({space:12}){Button(✏️ 画图).height(36).fontSize(13).backgroundColor(this.isEraseMode?#E5E5EA:#007AFF).fontColor(this.isEraseMode?#333333:#FFFFFF).borderRadius(18).onClick((){this.isEraseModefalse;})Button( 擦除).height(36).fontSize(13).backgroundColor(this.isEraseMode?#FF3B30:#E5E5EA).fontColor(this.isEraseMode?#FFFFFF:#333333).borderRadius(18).onClick((){this.isEraseModetrue;})Button( 清空).height(36).fontSize(13).backgroundColor(#8E8E93).fontColor(#FFFFFF).borderRadius(18).onClick((){this.clearAll();})}.margin({bottom:15})// 10×10 像素网格Grid(){ForEach(this.grid,(value:number,index:number){GridItem(){Column().width(100%).height(100%).backgroundColor(this.getCellColor(value)).border({width:1,color:#E0E0E0}).onClick((){this.paintCell(index);})}},(value:number,index:number)index.toString())}.columnsTemplate(1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr).rowsGap(0).columnsGap(0).width(280).height(280)Text(提示: 点击格子填充颜色支持画图/擦除模式切换).fontSize(12).fontColor(#AAAAAA).margin({top:20})}.width(100%).height(100%).justifyContent(FlexAlign.Start).alignItems(HorizontalAlign.Center)}}三、核心技术解析3.1 数组状态驱动 UI100 个像素的数据结构画板的核心数据是一个长度为 100 的数字数组Stategrid:number[]newArray(100).fill(0);每个元素代表一个像素格子的状态0空白默认颜色#F0F0F01已着色使用当前选中的颜色new Array(100).fill(0)一次性创建 100 个元素并初始化为 0。这是 JavaScript 创建固定大小数组并填充默认值的最简洁方式。数组索引 → 屏幕坐标的映射索引 i → 行 Math.floor(i / 10) 列 i % 10 例如 i 23: 行 Math.floor(23 / 10) 2 列 23 % 10 3 → 第 3 行第 4 列从 0 开始编号这种一维数组映射二维网格的技术在游戏开发如贪吃蛇、2048、扫雷中广泛使用。单元格颜色读取函数getCellColor(value:number):string{returnvalue1?this.currentColor:#F0F0F0;}这个函数的精妙之处在于当value 1时它返回的是当前的currentColor。这意味着如果用户先画了一些蓝色格子然后切换颜色为红色之前画的蓝色格子仍然是蓝色因为grid[i]的值保持为 1而getCellColor返回的是当前的currentColor……等等这其实是一个 bug发现问题了吗当value 1时getCellColor总是返回当前的currentColor而不是格子被涂色时的颜色。这意味着用户用蓝色画了几个格子grid[0]1, grid[1]1用户切换到红色currentColor #FF3B30之前蓝色的格子变成了红色——因为getCellColor(1)总是返回this.currentColor修复方案将grid改为存储具体的颜色值而非简单的 0/1 标记Stategrid:string[]newArray(100).fill();paintCell(index:number):void{this.grid[index]this.isEraseMode?:this.currentColor;}getCellColor(value:string):string{returnvalue||#F0F0F0;// 空字符串 → 底色#XXXXXX → 该格子的颜色}这样每个格子独立存储自己的颜色值切换颜色后不会影响已画好的格子。这是原始代码的一个设计缺陷也是从Demo 代码到生产级代码需要跨越的鸿沟。3.2 Grid 的columnsTemplate精确控制columnsTemplate是Grid组件最强大的布局属性Grid().columnsTemplate(1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr).rowsGap(0).columnsGap(0)columnsTemplate的语法类似 CSS Grid 的grid-template-columns语法含义示例1fr比例单位fraction3 个1fr 三等分px固定像素50px 固定 50 像素%百分比20% 父容器宽度的 20%repeat(n, 1fr)重复部分版本支持1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr表示 10 列等宽的网格。当 Grid 总宽度为 280px 时每列宽度为 28px每个格子是一个 28×28 的正方形。rowsGap(0)和columnsGap(0)将间距设为 0格子之间仅通过 1px 的border分隔。使用border而非gap的原因在于边框在每个格子自己的区域内不影响整体布局的精确计算。3.3 画图/擦除模式切换画板支持两种交互模式通过布尔值isEraseMode控制StateisEraseMode:booleanfalse;paintCell(index:number):void{this.grid[index]this.isEraseMode?0:1;}模式切换的 UI 表现画图模式isEraseMode false画图按钮蓝色#007AFF白色文字——表示当前激活擦除按钮灰色#E5E5EA深色文字——表示非激活擦除模式isEraseMode true画图按钮灰色深色文字擦除按钮红色#FF3B30白色文字——表示当前激活Button(✏️ 画图).backgroundColor(this.isEraseMode?#E5E5EA:#007AFF).fontColor(this.isEraseMode?#333333:#FFFFFF)Button( 擦除).backgroundColor(this.isEraseMode?#FF3B30:#E5E5EA).fontColor(this.isEraseMode?#FFFFFF:#333333)这是互斥按钮组的典型实现方式——用一个状态变量控制两个按钮的样式。3.4 颜色预设选择画板提供了 7 种预设颜色使用ForEach渲染privatecolors:string[][#FF3B30,#FF9500,#FFCC00,#34C759,#007AFF,#AF52DE,#000000];颜色预设的选中态通过边框宽度和颜色来实现Column().width(28).height(28).backgroundColor(color).borderRadius(14).border({width:this.currentColorcolor!this.isEraseMode?3:1,color:this.currentColorcolor!this.isEraseMode?#333333:#DDDDDD})当某个颜色是当前选中色且不在擦除模式时该颜色圆点显示 3px 深色边框否则显示 1px 浅色边框。条件this.currentColor color !this.isEraseMode确保当用户切换到擦除模式时所有颜色预设都不显示选中状态——因为擦除模式下选色是无效的。四、Grid 组件与其他布局的对比在 ArkTS 中创建网格布局有多种方式方法优点缺点适用场景GridcolumnsTemplate精确控制行列自动换行需要指定列数像素画板、九宫格、图片墙Column 多个Row每行可不同布局手动管理行数计算器按键、表单Flexwrap自动换行控制不够精细标签、徽章对于画板这种规整的网格每行每列数量固定、格子大小完全一致Grid是最佳选择。五、清空操作数组替换 vs 逐元素赋值clearAll():void{this.gridnewArray(100).fill(0);}这里通过整体替换数组而不是for循环逐个赋值来实现清空。这是有意为之的整体替换this.gridnewArray(100).fill(0);// ✅ 触发 State 更新逐个赋值不可行for(leti0;i100;i){this.grid[i]0;// ⚠️ 100次触发更新性能极差}// 或者this.grid.fill(0);// ⚠️ 部分 ArkTS 版本不支持ArkTS 的State对数组的代理支持直接索引赋值this.grid[i] x但 100 次for循环赋值会导致 100 次 UI 更新性能开销巨大。整体替换一次触发一次更新效率极高。六、扩展方向支持 Canvas 自由绘制集成Canvas组件支持手指拖拽连续绘制而非逐格点击填充工具实现油漆桶功能点击后填充相邻同色区域Flood Fill 算法保存与分享将像素画板内容导出为图片通过分享 Kit 分享撤销/重做保存操作栈Command Pattern支持 CtrlZ 撤销多种笔刷大小支持 1×1、2×2、3×3 笔刷对称绘制水平/垂直对称模式画一笔出现对称的两笔导入模板加载预设图案模板爱心、笑脸、星星等七、本章小结知识点掌握程度一维数组映射二维网格✅ 理解Math.floor(i/w)i%w映射columnsTemplate(1fr ...)✅ 掌握 Grid 精确列控制画图/擦除模式切换✅ 掌握互斥按钮的设计模式颜色预设选中态高亮✅ 理解条件边框样式数组整体替换 vs 逐元素赋值✅ 理解性能差异new Array(100).fill(0)✅ 掌握固定大小数组的创建简易画板展示了 ArkTS 中用数据驱动网格 UI的经典模式以及模式切换的设计思想。虽然在真正的绘图应用中 Canvas 更为合适但 Grid 方式在学习数组状态管理和网格布局方面有独特的教学价值。

相关新闻

2026/8/25 23:12:01

计算机毕业设计之基于Transformer的图像去雾算法研究与实现

基于Transformer的图像去雾算法研究与实现是一篇探讨何运用Transformer深度学习架构来提升图像去雾效果的研究论文。该文首先对当前图像去雾技术进行了全面的回顾与分析,指出了传统方法在处理复杂场景和细节保持上的局限性。随后,论文提出了一种新颖的基…

2026/8/26 3:53:42

Gemini 3 Flash口述即原型:重构产品冷启动工作流

1. 这不是“又一个AI模型”,而是原型设计工作流的断层式重构 Gemini 3 Flash 这个名字刚出来时,我第一反应是点开谷歌官方博客——不是为了看技术白皮书,而是想确认这到底是不是一次营销话术的升级。毕竟,“口述即原型”这个短语太…

2026/8/26 6:19:49

实测Kimi K2.7 Code高速版:四大工程任务检验AI编程实战力

1. 项目概述:当Kimi遇上代码工程最近圈子里关于Kimi K2.7 Code高速版的讨论挺热闹,作为一个常年和代码、项目打交道的开发者,我对这类宣称能“进工作流”的工具总是抱着既期待又审慎的态度。期待的是它能真正提升效率,审慎的是怕它…

2026/8/26 6:19:49

Verilog中[]与{}符号的硬件映射原理与工程实践指南

1. 项目概述:从两个符号窥见Verilog的设计哲学在FPGA和数字IC设计的入门阶段,很多工程师,包括当年的我,都会对Verilog HDL中那两个看似简单的符号——方括号[]和花括号{}——感到一丝困惑。它们无处不在,从最基本的线网…

2026/8/26 6:19:49

用Arduino自制乐高PF火车红外控制器:从协议到实车测试

如果你家里有一套乐高动力功能(Power Functions,简称 PF)系列的火车,你一定经历过这样的场景:拿着原装红外遥控器蹲在轨道旁边,生怕角度偏一点火车就不理你。乐高官方那个遥控器确实能用,但手感…

2026/8/26 6:19:49

深度学习光伏功率预测系统:从LSTM注意力到前后端全链路实现

简介:时间序列预测是新能源发电领域的核心难题,尤其是在光伏场景下,辐照度突变、云层遮挡等因素让功率曲线呈现高波动性和强周期性,传统统计方法往往难以应对。深度学习凭借端到端的非线性建模能力,能够从历史功率、气…

2026/8/26 6:19:49

Linux Pthread多线程编程:从基础概念到线程安全队列实战

1. 从“Hello World”到并发世界:为什么Pthread是Linux开发的必修课如果你在Linux下写过C/C程序,并且想让你的程序同时做两件或更多的事情——比如一边从网络接收数据,一边更新用户界面,或者同时处理多个文件——那么你迟早会碰到…

2026/8/26 6:14:49

Granite 4模型如何颠覆嵌入式开发?本地AI编程助手实战

我这些年一直在折腾嵌入式开发,从8051到Cortex-M再到RISC-V,写过的底层代码不算少,调试过的板子也堆满了半个工作台。说实话,嵌入式开发这个圈子,工具链的变化其实很慢,几十年了还是C语言打底,寄…

2026/8/25 1:04:19

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/25 11:48:27

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/25 16:56:43

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/26 0:04:32

Python random 模块常用函数详解:从入门到实战

目录 1. 引言2. 准备工作3. 基础随机函数4. 序列相关函数5. 随机种子与复现6. 实战案例7. 注意事项8. 常见问题与排查9. 总结 1. 引言 摘要: 本文系统介绍 Python 标准库 random 模块中最常用的随机数生成函数。内容涵盖基础随机函数(random()、unifor…

2026/8/26 1:19:35

JSON总结

JSON概念 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,主要用于跟服务器进行交换数据。它基于ECMAScript的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C、C、C#、Java、JavaScr…

2026/8/26 1:19:35

保存连接sse 是什么原理,为什么不会一直请求

“保持连接”用的是 SSE(Server-Sent Events),本质是一个没有马上结束的 HTTP 请求。 过程是: 拷贝机发送一次请求: GET /api/code-sync/events服务器返回: Content-Type: text/event-stream但不关闭响应&…

2026/8/24 13:42:17

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/24 18:13:48

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/25 1:08:14

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…