HarmonyOS APP实战-画图APP - 第7篇:文本绘制功能

发布时间:2026/9/25 10:43:14

HarmonyOS APP实战-画图APP - 第7篇:文本绘制功能
HarmonyOS APP实战-画图APP - 第7篇在画布上添加文字1. 开篇在第6篇中我们完成了绘制模式的切换与交互反馈优化。通过DrawMode枚举Rect、Circle、FreeDraw管理当前绘制模式在工具栏中实现了高亮选中状态并在画布触摸事件中根据模式调用不同的绘制逻辑drawRect、drawCircle、drawPath。顶部显示当前模式标签用户操作时获得明确的视觉反馈。本篇我们将为画图APP增加文字绘制功能。文字是图形创作中不可或缺的元素——添加标题、批注、签名等场景都需要它在画布上呈现。实现思路是用户通过一个弹出窗口输入文字、选择字体大小和颜色然后在画布上点击或拖拽放置文本。我们将使用ohos.graphics.drawing模块的drawTextAPI在Canvas上渲染文字并实现可拖拽定位的交互。2. 核心实现2.1 基础配置首先导入绘制文字所需的模块并扩展我们的状态管理。在Index.ets顶部添加导入。// Index.ets — 基础配置与模块导入import{CanvasRenderingContext2D}fromohos.graphics.drawing;// 定义文字对象的数据结构interfaceTextItem{content:string;// 文字内容fontSize:number;// 字体大小pxcolor:string;// 颜色十六进制字符串x:number;// 文本左上角x坐标y:number;// 文本左上角y坐标width?:number;// 文本渲染宽度用于碰撞检测height?:number;// 文本渲染高度用于碰撞检测}// 扩展DrawMode枚举添加TEXT模式enumDrawMode{Rect,Circle,FreeDraw,Text// 新增文字模式}关键点说明TextItem接口存储文字的所有属性width和height用于触摸命中检测判断用户是否点到文本区域。DrawMode.Text作为新的绘制模式与之前的形状/画笔模式平级。后续我们需要在画布渲染循环中遍历TextItem数组并调用drawText。2.2 FontPicker组件字体大小与颜色选择器用户输入文字时需要同时选择字体大小和颜色。我们创建一个独立的FontPicker组件它显示一个自定义弹窗或内嵌UI。// components/FontPicker.ets — 字体大小与颜色选择器Componentexportstruct FontPicker{ConsumefontSize:number;// 从父组件继承的字体大小ConsumefontColor:string;// 从父组件继承的字体颜色build(){Column(){// 字体大小滑块Row(){Text(字体大小:).fontSize(16).width(80)Slider({value:this.fontSize,min:12,max:120,step:4,style:SliderStyle.OutSet}).width(200).onChange((value:number){this.fontSizevalue;// 通过Consume双向绑定})}.margin({bottom:12})// 颜色选择器预设色块Row(){Text(字体颜色:).fontSize(16).width(80)// 使用Grid展示常用颜色ForEach([#000000,#FF0000,#00FF00,#0000FF,#FFFF00,#FF00FF,#00FFFF,#FFFFFF],(color:string){Circle({width:30,height:30}).fill(color).stroke(this.fontColorcolor?Color.Black:Color.White).strokeWidth(3).margin(4).onClick((){this.fontColorcolor;})})}}.padding(12).backgroundColor(#F5F5F5).borderRadius(8)}}关键点说明使用Consume装饰器实现父子组件双向数据绑定父组件提供fontSize和fontColor两个状态变量。Slider组件控制字体大小范围12~120px步进4px。颜色选择器用8个预设色块点击直接赋值实际项目可扩展为完整的色盘参考第5篇的颜色选择逻辑。此组件被嵌入到文本输入弹窗中跟随弹窗显示。2.3 文本输入与拖拽放置模块TextTool核心逻辑当用户点击工具栏的“文字”按钮后模式切换为Text。点击画布任意位置弹出输入框让用户输入文字内容并确认。文字对象随即出现在点击位置用户可继续拖拽调整位置。// components/TextTool.ets — 文字输入与拖拽放置模块Componentexportstruct TextTool{privatecanvasCtx:CanvasRenderingContext2D;LinktextItems:TextItem[];// 画布上所有文字对象StatefontSize:number36;// 当前字体大小StatefontColor:string#000000;// 当前字体颜色StateisAddingText:booleanfalse;// 是否正在输入文字StateinputContent:string;// 输入框内容privatetextDialogController:CustomDialogController;build(){Column(){// 文字模式专用按钮Button(添加文字).fontSize(16).onClick((){this.isAddingTexttrue;// 弹出自定义对话框用于输入文字this.showTextInputDialog();})}}// 显示文字输入对话框使用CustomDialogshowTextInputDialog(){this.textDialogControllernewCustomDialogController({builder:TextInputDialog({fontSize:this.fontSize,fontColor:this.fontColor,onConfirm:(content:string){this.inputContentcontent;this.isAddingTexttrue;this.textDialogController.close();},onCancel:(){this.isAddingTextfalse;this.textDialogController.close();}}),autoCancel:true,alignment:DialogAlignment.Center,offset:{dx:0,dy:-80}});this.textDialogController.open();}// 在画布上放置文字的触摸事件处理handleCanvasTouch(event:TouchEvent):void{if(this.isAddingTextthis.inputContent){// 获取触摸点坐标相对于画布consttouchXevent.touches[0].x;consttouchYevent.touches[0].y;// 创建一个新的TextItem并添加到textItems数组constnewText:TextItem{content:this.inputContent,fontSize:this.fontSize,color:this.fontColor,x:touchX,y:touchY};this.textItems[...this.textItems,newText];// 重置状态准备下一次输入this.inputContent;this.isAddingTextfalse;// 重绘画布this.redrawCanvas();}else{// 尝试拖拽已有的文字对象this.dragTextItem(event);}}// 拖拽文字在触摸移动时更新坐标dragTextItem(event:TouchEvent){consttouchXevent.touches[0].x;consttouchYevent.touches[0].y;// 遍历文字列表检查触摸点是否落在某个文字区域内for(letithis.textItems.length-1;i0;i--){constitemthis.textItems[i];// 计算文字区域估算宽度content长度*fontSize*0.6高度fontSize*1.2constestimatedWidthitem.content.length*item.fontSize*0.6;constestimatedHeightitem.fontSize*1.2;// 判断触摸点是否在文字矩形区域内if(touchXitem.xtouchXitem.xestimatedWidthtouchYitem.y-estimatedHeighttouchYitem.y){// 更新坐标if(event.typeTouchType.Move){item.xtouchX-estimatedWidth/2;item.ytouchY;this.textItems[...this.textItems];// 触发UI更新this.redrawCanvas();}break;}}}// 重新绘制画布包含所有文字redrawCanvas(){if(!this.canvasCtx)return;// 清空画布并重新绘制所有图形此处简化实际需结合之前的绘制指令this.canvasCtx.clearRect(0,0,this.canvasCtx.width,this.canvasCtx.height);// ... 重新绘制之前的形状 画笔轨迹省略与第3-6篇代码集成this.drawAllTexts();}// 遍历文字列表绘制所有文字drawAllTexts(){if(!this.canvasCtx)return;this.textItems.forEach((item:TextItem){this.canvasCtx.font${item.fontSize}px sans-serif;this.canvasCtx.fillStyleitem.color;this.canvasCtx.textAlignleft;this.canvasCtx.textBaselinetop;this.canvasCtx.fillText(item.content,item.x,item.y);});}}关键点说明使用CustomDialogController弹出输入对话框TextInputDialog组件需另定义用于输入文字内容。触摸事件处理当处于“添加文字”状态且内容非空时触摸点即文字放置位置否则尝试拖拽已有文字。estimatedWidth和estimatedHeight是粗略估算的文字区域用于触摸命中判断。精确计算可使用CanvasRenderingContext2D.measureText但因异步问题本实现采用估算方式仍可满足基本交互。drawAllTexts方法在每次重绘时调用使用fillText绘制文字。font、fillStyle依据TextItem属性动态设置。2.4 在画布组件中集成文字绘制最后将TextTool集成到主画布组件中在渲染循环中调用文字绘制。// Index.ets — 主画布组件核心代码片段Componentstruct DrawingCanvas{privatecanvasCtx:CanvasRenderingContext2DnewCanvasRenderingContext2D();StatecurrentMode:DrawModeDrawMode.Rect;StatetextItems:TextItem[][];// 文字对象数组privatetextTool:TextToolnewTextTool();build(){Column(){// ... 工具栏省略包含模式切换按钮// 文字模式下显示TextTool组件if(this.currentModeDrawMode.Text){TextTool({canvasCtx:this.canvasCtx,textItems:$textItems}).width(100%).height(60)}// 画布区域Canvas(this.canvasCtx).width(100%).layoutWeight(1).backgroundColor(#FFFFFF).onTouch((event:TouchEvent){if(this.currentModeDrawMode.Text){this.textTool.handleCanvasTouch(event);}else{// 原有形状/画笔模式处理略}})}.width(100%).height(100%)}}关键点说明TextItem数组通过State声明驱动UI更新。触摸事件根据currentMode分发文字模式调用TextTool.handleCanvasTouch其他模式走原有逻辑。TextTool组件接收canvasCtx和textItems引用实现跨组件的协作。3. 运行验证在DevEco Studio中运行项目按照以下步骤验证点击工具栏新增的“文字”按钮模式切换为文字模式按钮高亮。点击画布任意位置弹出文字输入对话框。输入文字内容例如“Hello HarmonyOS”通过滑块调整字体大小为48px选择红色。点击“确定”后输入的文字出现在点击位置。再次在文字区域内触摸并拖动文字跟随手指移动。重复上述步骤添加多个文字可叠加在之前的图形之上。预期表现文字清晰渲染颜色和大小与设置一致拖拽行为流畅触摸命中区域基本准确。4. 小结与预告本篇完成了文本绘制功能在画图APP中的集成。核心产出包括定义了TextItem数据结构支持存储内容、字体、颜色、坐标。实现了FontPicker组件提供字体大小和颜色选择UI。实现了TextTool模块包含文字输入对话框、触摸放置和拖拽逻辑。使用CanvasRenderingContext2D.fillTextAPI渲染文字并与原有绘制逻辑共存。至此画图APP已支持矩形、圆形、自由画笔和文字四种绘制元素用户的创作能力大大增强。下一篇《使用effectKit处理图像》将引入ohos.effectKit模块为画布内容添加亮度调节、模糊化、灰度调节等图像特效。届时用户在绘制完成后可选择应用滤镜实现更丰富的视觉效果。我们将在第8篇中构建特效面板并集成到工具栏中。

相关新闻

Open Source Candies生态系统分析:开源项目如何利用免费服务成长

Open Source Candies生态系统分析:开源项目如何利用免费服务成长

2026/9/1 11:23:48

Open Source Candies生态系统分析:开源项目如何利用免费服务成长 【免费下载链接】opensource-candies Free stuff for open source projects 项目地址: https://gitcode.com/gh_mirrors/op/opensource-candies 在开源项目的成长过程中,资源和工具…

Orchestra工具生态系统全解析:从内置工具到自定义工具开发

Orchestra工具生态系统全解析:从内置工具到自定义工具开发

2026/9/6 12:40:03

Orchestra工具生态系统全解析:从内置工具到自定义工具开发 【免费下载链接】orchestra Cognitive Architectures for Multi-Agent Teams 项目地址: https://gitcode.com/gh_mirrors/orchestr/orchestra Orchestra是一个强大的多智能体协作框架,其…

Osg 开发一:OpenSceneGraph 介绍及开发环境设置

Osg 开发一:OpenSceneGraph 介绍及开发环境设置

2026/9/25 8:27:26

文章目录一 OpenSceneGraph 简介1 什么是 OpenSceneGraph2 OSG 中国3 OSG 组成模块二 OSG 开发1 开发环境2 环境设置3 Hello OSG 工程其他一 OpenSceneGraph 简介 1 什么是 OpenSceneGraph OSG 是一个开源的场景图形管理开发库,主要为图形图像应用程序的开发提供场…

CANN/GE ACL数据集缓冲区添加函数

CANN/GE ACL数据集缓冲区添加函数

2026/9/25 10:06:33

aclmdlAddDatasetBuffer 【免费下载链接】ge GE(Graph Engine)是面向昇腾的图编译器和执行器,提供了计算图优化、多流并行、内存复用和模型下沉等技术手段,加速模型执行效率,减少模型内存占用。 GE 提供对 PyTorch、Te…

用ffmpeg高效批量调整图片尺寸的实战指南

用ffmpeg高效批量调整图片尺寸的实战指南

2026/9/25 9:40:47

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

Transformers 音频特征提取工具库 audio_utils 全解析:从 Mel 刻度换算到对数 Mel 频谱

Transformers 音频特征提取工具库 audio_utils 全解析:从 Mel 刻度换算到对数 Mel 频谱

2026/9/25 10:06:21

Transformers 音频特征提取工具库 audio_utils 全解析:从 Mel 刻度换算到对数 Mel 频谱 【免费下载链接】transformers 🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and mu…

RustFS 多节点集群重启与滚动升级实战:Readiness、Quorum 与 Degraded 模式完全指南

RustFS 多节点集群重启与滚动升级实战:Readiness、Quorum 与 Degraded 模式完全指南

2026/9/25 9:53:52

RustFS 多节点集群重启与滚动升级实战:Readiness、Quorum 与 Degraded 模式完全指南 【免费下载链接】rustfs 🚀2.3x faster than MinIO for 4KB object payloads. RustFS is an open-source, S3-compatible high-performance object storage system sup…

Java Integer缓存揭秘:128陷阱原理、避坑与面试全解

Java Integer缓存揭秘:128陷阱原理、避坑与面试全解

2026/9/25 8:58:17

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

RustFS Scanner 数据用量发布权威性决策:配额准入如何获得可用的权威依据

RustFS Scanner 数据用量发布权威性决策:配额准入如何获得可用的权威依据

2026/9/25 10:00:17

RustFS Scanner 数据用量发布权威性决策:配额准入如何获得可用的权威依据 【免费下载链接】rustfs 🚀2.3x faster than MinIO for 4KB object payloads. RustFS is an open-source, S3-compatible high-performance object storage system supporting mi…

远程协作的工作台整理

远程协作的工作台整理

2026/9/24 16:02:49

远程协作的工作台整理远程协作的核心不是再加一个工具,而是让交接信息足够完整。异步任务要写明目标、输入位置、完成标准和需要决策的人。 工作台的最小配置 将日程、待办、代码和沟通入口收拢到少数固定位置;通知按紧急程度分层。工作台不需要模仿办公…

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

2026/9/25 9:41:47

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

2026/9/25 4:22:14

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…