news 2026/7/13 1:59:53

HarmonyOS APP实战-画图APP - 第2篇:核心框架搭建

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HarmonyOS APP实战-画图APP - 第2篇:核心框架搭建

HarmonyOS APP实战-画图APP - 第2篇:主页面布局与基础交互

1. 开篇

在上一篇《准备开发环境与创建项目》中,我们完成了HarmonyOS开发环境的搭建,使用DevEco Studio创建了画图APP项目工程,并成功运行了默认的Hello World页面。在EntryAbility中配置了全屏显示,项目基础结构已准备就绪。

本文将聚焦于画图APP的核心界面搭建。我们将设计一个三栏式布局:顶部工具栏(绘制模式切换、撤销重做按钮)、中间画布区域(承载所有绘制内容)、底部颜色选择栏(基础颜色选择)。同时,我们将集成Canvas组件,并为其绑定基础触摸事件,为后续章节的形状绘制、手绘功能打下坚实基础。

完成本篇后,你将看到一个完整的画图APP界面框架,具备响应式布局能力,可以在不同屏幕尺寸上自适应显示。

2. 核心实现

2.1 基础配置与数据模型设计

在开始界面布局之前,我们需要设计APP的数据模型。创建一个model文件夹,定义绘制模式和颜色相关的枚举与接口。

// model/DrawingData.ets// 绘制模式枚举:后续将扩展矩形、圆形、自由画笔等exportenumDrawMode{RECTANGLE='rectangle',// 矩形CIRCLE='circle',// 圆形FREEDRAW='freedraw',// 自由画笔NONE='none'// 无模式(初始状态)}// 画笔属性接口:管理当前画笔的颜色、粗细、透明度exportinterfaceBrushProperty{color:string;// 画笔颜色,格式:#RRGGBBlineWidth:number;// 画笔粗细,像素值opacity:number;// 透明度,0-1}

关键点说明

  • DrawMode枚举定义了三种绘制模式,后续将在工具栏中通过图标按钮切换
  • BrushProperty接口封装了画笔的核心属性,后续颜色选择器和粗细滑块将修改这些属性
  • 使用export关键字导出,便于其他模块引用

2.2 核心逻辑:主页面布局组件

Index.ets是整个APP的入口页面,负责组装ToolBar、Canvas和ColorPicker三个核心区域。我们需要实现响应式布局,确保在竖屏和横屏下都能正常显示。

// pages/Index.etsimport{DrawMode,BrushProperty}from'../model/DrawingData'import{ToolBar}from'./ToolBar'import{ColorPicker}from'./ColorPicker'@Entry@Componentstruct Index{// 当前绘制模式,默认无模式@StatecurrentMode:DrawMode=DrawMode.NONE// 画笔属性,初始颜色黑色,粗细5px,透明度1(不透明)@Statebrush:BrushProperty={color:'#000000',lineWidth:5,opacity:1}// Canvas画布的尺寸,通过onAreaChange动态获取@StatecanvasWidth:number=0@StatecanvasHeight:number=0build(){// 使用Column垂直布局,从上到下排列工具栏、画布、颜色选择器Column(){// 顶部工具栏:提供绘制模式切换、撤销重做ToolBar({currentMode:this.currentMode,brush:this.brush,onModeChange:(mode:DrawMode)=>{this.currentMode=mode}})// 中间画布区域:使用Stack包裹Canvas,以便后续添加触摸事件Stack(){// Canvas组件,后续绘制操作都在此进行Canvas(this.canvasContext).width('100%').height('100%').backgroundColor('#FFFFFF')// 获取Canvas的渲染上下文,用于绘制操作.onReady(()=>{console.info('Canvas 已就绪')})// 触摸事件:开始触摸时记录位置,用于后续绘制.onTouch((event:TouchEvent)=>{if(event.type===TouchType.Down){// 获取触摸点在Canvas内的坐标constx=event.touches[0].xconsty=event.touches[0].yconsole.info(`触摸点坐标: (${x},${y})`)// 后续章节将在此触发绘制逻辑}})}.layoutWeight(1)// 自动填充剩余空间.width('100%')// 底部颜色选择栏:提供基础颜色选择和透明度调节ColorPicker({brush:this.brush,onColorChange:(color:string)=>{this.brush={...this.brush,color:color}}})}.width('100%').height('100%').backgroundColor('#F5F5F5')// 监听容器尺寸变化,动态更新Canvas尺寸.onAreaChange((oldValue:Area,newValue:Area)=>{this.canvasWidth=newValue.widthasnumberthis.canvasHeight=newValue.heightasnumberconsole.info(`容器尺寸:${this.canvasWidth}x${this.canvasHeight}`)})}// Canvas渲染上下文,用于后续绘制privatecanvasContext:CanvasRenderingContext2D=newCanvasRenderingContext2D()}

关键点说明

  • @State装饰的变量用于声明式响应UI更新,brush对象的变化会自动触发颜色选择器和后续绘制组件的刷新
  • layoutWeight(1)让Canvas区域填充工具栏和颜色选择器之外的剩余空间
  • CanvasRenderingContext2D是HarmonyOS提供的2D渲染上下文,所有绘制操作都通过它完成。文档中提到的@ohos.graphics.drawing模块提供了更底层的绘制能力,我们将在后续篇章中结合使用
  • onAreaChange回调用于监听容器尺寸变化,确保Canvas能够自适应屏幕旋转和窗口调整

2.3 完整模块:工具栏组件

工具栏组件包含绘制模式切换按钮(矩形、圆形、自由画笔)和功能按钮(撤销、重做、清空)。按钮采用图标式设计,选中状态高亮显示。

// pages/ToolBar.etsimport{DrawMode,BrushProperty}from'../model/DrawingData'@Componentexportstruct ToolBar{// 当前绘制模式privatecurrentMode:DrawMode// 画笔属性privatebrush:BrushProperty// 模式切换回调privateonModeChange:(mode:DrawMode)=>voidbuild(){// 使用Row水平布局,均匀分布所有工具按钮Row(){// 矩形模式按钮Button(){Text('□').fontSize(24).fontColor(this.currentMode===DrawMode.RECTANGLE?'#FF6600':'#333333')}.width(40).height(40).backgroundColor(this.currentMode===DrawMode.RECTANGLE?'#E0E0E0':'#FFFFFF').borderRadius(8).onClick(()=>{this.onModeChange(DrawMode.RECTANGLE)})// 圆形模式按钮Button(){Text('○').fontSize(24).fontColor(this.currentMode===DrawMode.CIRCLE?'#FF6600':'#333333')}.width(40).height(40).backgroundColor(this.currentMode===DrawMode.CIRCLE?'#E0E0E0':'#FFFFFF').borderRadius(8).onClick(()=>{this.onModeChange(DrawMode.CIRCLE)})// 自由画笔模式按钮Button(){Text('✎').fontSize(24).fontColor(this.currentMode===DrawMode.FREEDRAW?'#FF6600':'#333333')}.width(40).height(40).backgroundColor(this.currentMode===DrawMode.FREEDRAW?'#E0E0E0':'#FFFFFF').borderRadius(8).onClick(()=>{this.onModeChange(DrawMode.FREEDRAW)})// 分隔线Divider().vertical(true).height(30).color('#CCCCCC').margin({left:10,right:10})// 撤销按钮Button(){Text('↩').fontSize(24).fontColor('#333333')}.width(40).height(40).backgroundColor('#FFFFFF').borderRadius(8).onClick(()=>{// 撤销功能将在第11篇实现console.info('撤销操作')})// 重做按钮Button(){Text('↪').fontSize(24).fontColor('#333333')}.width(40).height(40).backgroundColor('#FFFFFF').borderRadius(8).onClick(()=>{// 重做功能将在第11篇实现console.info('重做操作')})// 清空按钮Button(){Text('🗑').fontSize(24).fontColor('#333333')}.width(40).height(40).backgroundColor('#FFFFFF').borderRadius(8).onClick(()=>{// 清空画布功能console.info('清空画布')})}.width('100%').padding({left:16,right:16,top:8,bottom:8}).backgroundColor('#FFFFFF').justifyContent(FlexAlign.SpaceAround)// 添加阴影效果.shadow({radius:4,color:'#1A000000',offsetX:0,offsetY:2})}}

关键点说明

  • 按钮选中状态通过currentMode判断,矩形模式选中时文字颜色变为橙色(#FF6600),背景变为浅灰色,提供清晰的视觉反馈
  • Divider组件作为分隔线,将绘制模式按钮与功能按钮分开,提升界面层次感
  • shadow属性为工具栏添加了底部阴影,使其与画布区域产生视觉分离
  • 撤销、重做、清空按钮目前仅打印日志,具体逻辑将在后续章节实现

2.4 完整模块:颜色选择器组件

颜色选择器提供8种常用颜色和透明度滑块,用户点击颜色按钮即可切换画笔颜色。后续可扩展为自定义色盘。

// pages/ColorPicker.etsimport{BrushProperty}from'../model/DrawingData'@Componentexportstruct ColorPicker{// 画笔属性privatebrush:BrushProperty// 颜色变更回调privateonColorChange:(color:string)=>void// 颜色列表:8种常用颜色privatecolors:string[]=['#000000','#FF0000','#00FF00','#0000FF','#FFFF00','#FF00FF','#00FFFF','#808080']build(){// 使用Column垂直布局,上方是颜色按钮行,下方是透明度控制Column(){// 颜色按钮行Row(){ForEach(this.colors,(color:string)=>{Button(){// 颜色按钮显示当前颜色}.width(30).height(30).backgroundColor(color).borderRadius(15)// 圆形按钮.border({width:this.brush.color===color?3:1,color:this.brush.color===color?'#FF6600':'#CCCCCC'}).onClick(()=>{this.onColorChange(color)})},(color:string)=>color)}.width('100%').justifyContent(FlexAlign.SpaceAround).padding({top:8,bottom:8})// 透明度控制行:使用Slider滑块调节透明度Row(){Text('透明度').fontSize(14).fontColor('#666666').margin({right:8})Slider({value:this.brush.opacity*100,// Slider的值范围0-100min:0,max:100,step:1}).width('70%')// 滑块值变化时更新画笔透明度.onChange((value:number)=>{this.brush.opacity=value/100// 触发UI刷新this.brush={...this.brush}})}.width('100%').padding({left:16,right:16,bottom:8})}.width('100%').padding({left:16,right:16,top:8,bottom:8}).backgroundColor('#F0F0F0')}}

关键点说明

  • 颜色按钮通过backgroundColor直接显示对应颜色,使用borderRadius(15)将其变为圆形
  • 选中状态的判断:当前画笔颜色与按钮颜色一致时,边框变为橙色并加粗,提供清晰的选中指示
  • Slider组件用于透明度调节,其value范围是0-100,我们需要将其转换为0-1的透明度值存储在brush.opacity
  • 注意:修改brush对象的属性后,需要重新赋值this.brush = { ...this.brush }才能触发UI重绘,因为@State装饰器只监听引用变化,不监听属性变化

3. 运行验证

启动DevEco Studio,选择模拟器或真机运行项目。我们将看到以下预期效果:

  1. 页面布局:顶部显示工具栏,包含6个按钮(矩形、圆形、自由画笔、撤销、重做、清空);中间为白色画布区域,占据大部分屏幕;底部显示8个颜色按钮和透明度滑块

  2. 模式切换:点击工具栏中的矩形、圆形或自由画笔按钮,对应的图标会变为橙色高亮,背景变为灰色,表示当前选中状态

  3. 颜色选择:点击底部颜色按钮,画笔颜色会立即切换,选中按钮显示橙色边框。拖动透明度滑块,可以看到滑块值相应变化

  4. 触摸响应:在画布上触摸时,控制台会打印触摸点的坐标信息,表明触摸事件已正确绑定

实际运行效果如下图所示:

如果遇到布局错乱,可以检查模拟器的屏幕尺寸设置,建议使用Phone(1080x1920)模板。你也可以在项目config.json中配置"orientation": "portrait"暂时锁定竖屏。

4. 小结与预告

本篇成功搭建了画图APP的主界面框架,完成了以下核心工作:

  • 设计了DrawMode枚举和BrushProperty接口作为数据模型
  • 实现了Index.ets主页面,采用三栏式布局(工具栏 + 画布 + 颜色选择器)
  • 开发了ToolBar.ets组件,支持绘制模式切换和功能按钮占位
  • 开发了ColorPicker.ets组件,提供颜色选择和透明度调节
  • 集成了Canvas组件并绑定了基础触摸事件,画布尺寸可自适应

至此,画图APP的基础交互框架已经完成,所有UI元素都处于响应式状态,为后续绘制功能的实现做好了准备。

下一篇「实现基本形状绘制」将聚焦于CanvasRenderingContext2D的绘制API,实现矩形、正方形和圆形的绘制逻辑。我们将在触摸事件中获取起点和终点坐标,利用drawRectdrawCircle方法在画布上生成基本图形。敬请期待!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/13 1:59:35

从零开始:大气层整合包系统如何让Switch破解变得简单又安全

从零开始:大气层整合包系统如何让Switch破解变得简单又安全 【免费下载链接】Atmosphere-stable 大气层整合包系统稳定版 项目地址: https://gitcode.com/gh_mirrors/at/Atmosphere-stable 还在为Switch破解的复杂流程感到头疼吗?大气层整合包系统…

作者头像 李华
网站建设 2026/7/13 1:58:18

2026 论文AI率助手横向对比:公认好用的,科研党救急指南

2026 年学术审查全面升级,AIGC 检测率与重复率双重加码,知网、万方系统更新后,传统改写方式易被识别。结合降重效果、AI 痕迹清除、格式保留、使用便捷性、性价比五大核心指标,实测 5 款热门工具,助你精准应对挑战&…

作者头像 李华
网站建设 2026/7/13 1:56:32

题型归纳与重要模版

文章目录题型归纳重要模版题型归纳 子数组/子字符串 -> 滑动窗口算法 和为 xxx 的子数组 -> 前缀和与哈希表的结合使用 带权重的随机选择算法 -> 前缀和与二分搜索 两个字符串求子序列 -> 动态规划(用两个指针 i 和 j 分别在两个字符串上移动&#…

作者头像 李华
网站建设 2026/7/13 1:53:32

工业负载控制:TPD2017FN与PIC18F57K42的智能驱动方案

1. 工业负载控制的核心挑战与解决方案在工业自动化现场,我经常遇到工程师们对电感和电阻负载控制的困惑。记得去年在一条包装生产线上,他们的继电器控制系统平均每周都会烧毁一个驱动模块,直到我们引入了TPD2017FN和PIC18F57K42的组合方案。这…

作者头像 李华
网站建设 2026/7/13 1:51:30

C++14数字分隔符:提升代码可读性的零开销语法糖

1. 项目概述:为什么我们需要数字分隔符?写C代码,尤其是处理金融、图形、嵌入式或者任何需要大量硬编码数字常量的领域时,你肯定遇到过这样的场景:面对一长串数字,比如1000000000,你得在心里默默…

作者头像 李华