文章目录前言先把业务看明白状态和 UI 的关系核心逻辑拆读片段 1get cols(): number {片段 2get currentFilter(): FilterPreset_wc1v {使用方式完整代码一个小复盘前言这个案例看着是一个小页面里面其实塞了不少常见业务写法列表、筛选、按钮事件、条件样式基本都能练到。它的主线是图片九宫格切割细节落在正方形裁剪预览与发布效果。这些细节刚好能把页面状态、用户操作和组件刷新串起来。先把业务看明白NineGridImageSplitPage不是一个只展示静态内容的页面。它会根据用户点击、输入、切换或计时来改变界面所以阅读代码时可以先抓三条线观察点在这个案例里的表现页面主题图片九宫格切割主要文案原图,清新,复古,黑白,暖调,冷调常用组件Button,Column,Divider,ForEach,Grid,GridItem,Row,Scroll,Slider,Stack,Text适合练习状态驱动 UI、条件样式、列表渲染、事件回调状态和 UI 的关系ArkUI 的页面刷新依赖状态变化。下面这些State字段就是页面的“开关”和“数据源”不用手动找 DOM也不用自己通知某个控件刷新。状态字段类型我会怎么理解它selectedCountnumber当前选中项决定高亮和内容切换filterIndexnumber当前选中项决定高亮和内容切换previewModeboolean记录页面当前选择、输入或展示状态spacingnumber记录页面当前选择、输入或展示状态一个经验先把State看完再看build()页面逻辑会清楚很多。否则很容易被一长串布局代码带偏。核心逻辑拆读片段 1get cols(): number {这段代码承担的是页面里的“判断”或“动作”把它从布局里抽出来后build()会清爽很多。后面要加新条件也不会到处翻 UI 代码。getcols():number{if(this.selectedCount1)return1if(this.selectedCount4)return2return3}片段 2get currentFilter(): FilterPreset_wc1v {这段代码承担的是页面里的“判断”或“动作”把它从布局里抽出来后build()会清爽很多。后面要加新条件也不会到处翻 UI 代码。getcurrentFilter():FilterPreset_wc1v{returnthis.filterPresets[this.filterIndex]}使用方式把下面代码放进 ArkTS 页面文件中即可运行。用于正式项目时我会继续把数据模型、卡片 Builder 和页面事件拆出去页面文件只负责组合。建议练习顺序先改状态字段 - 再改交互事件 - 最后调整 UI 样式 检查重点点击是否刷新、列表 key 是否稳定、条件样式是否集中管理这份代码可以继续扩展的方向把模拟数据替换成接口数据抽出复用卡片组件增加空状态和异常状态补充深色模式或主题色适配对输入类场景增加校验提示完整代码// NineGridImageSplitPage - 图片九宫格切割 - 正方形裁剪预览与发布效果interfaceGridCell{row:numbercol:numberbgX:numberbgY:number}interfaceFilterPreset_wc1v{name:stringbrightness:numbercontrast:numbersaturate:numbergrayscale:number}EntryComponentstruct NineGridImageSplitPage{StateselectedCount:number9StatefilterIndex:number0StatepreviewMode:booleanfalseStatespacing:number4privatefilterPresets:FilterPreset_wc1v[][{name:原图,brightness:1.0,contrast:1.0,saturate:1.0,grayscale:0},{name:清新,brightness:1.1,contrast:0.9,saturate:1.3,grayscale:0},{name:复古,brightness:0.9,contrast:1.1,saturate:0.7,grayscale:0.2},{name:黑白,brightness:1.0,contrast:1.2,saturate:0,grayscale:1.0},{name:暖调,brightness:1.05,contrast:1.0,saturate:1.2,grayscale:0},{name:冷调,brightness:0.95,contrast:1.1,saturate:0.9,grayscale:0},]privatecolorPalette:string[][#FF6B9D,#C44569,#F8B500,#6C5CE7,#00B894,#0984E3,#E17055,#74B9FF,#A29BFE,]getcols():number{if(this.selectedCount1)return1if(this.selectedCount4)return2return3}getcurrentFilter():FilterPreset_wc1v{returnthis.filterPresets[this.filterIndex]}getCells():GridCell[]{constcells:GridCell[][]for(letr0;rthis.cols;r){for(letc0;cthis.cols;c){if(cells.lengththis.selectedCount){cells.push({row:r,col:c,bgX:c,bgY:r})}}}returncells}build(){Column({space:0}){// 顶部导航Row(){Text(✕).fontSize(20).fontColor(#1a1a1a)Blank()Text(九宫格切割).fontSize(18).fontWeight(FontWeight.Bold).fontColor(#1a1a1a)Blank()Text(发布).fontSize(16).fontColor(#1890ff).fontWeight(FontWeight.Bold)}.width(100%).padding({left:16,right:16,top:14,bottom:14}).backgroundColor(#ffffff)Scroll(){Column({space:20}){// 源图预览用颜色渐变模拟Column(){Text(源图预览).fontSize(13).fontColor(#999999).padding({bottom:10}).width(100%)Stack(){Column().width(100%).height(280).linearGradient({angle:135,colors:[[#FF6B9D,0],[#C44569,0.3],[#F8B500,0.6],[#6C5CE7,1.0]]}).borderRadius(12).brightness(this.currentFilter.brightness).contrast(this.currentFilter.contrast).saturate(this.currentFilter.saturate).grayscale(this.currentFilter.grayscale)// 九宫格切割线if(this.selectedCount9){Column(){Divider().strokeWidth(2).color(#ffffff80).margin({top:93})Divider().strokeWidth(2).color(#ffffff80).margin({top:93})}.width(100%).height(280).position({x:0,y:0})Row(){Divider().width(2).height(280).backgroundColor(#ffffff80).margin({left:33%})Divider().width(2).height(280).backgroundColor(#ffffff80).margin({left:33%})}.width(100%).height(280).position({x:0,y:0})}}.width(100%)}.padding({left:16,right:16})// 滤镜选择Column({space:10}){Text(滤镜效果).fontSize(14).fontWeight(FontWeight.Medium).fontColor(#1a1a1a).padding({left:16}).width(100%)Scroll(){Row({space:12}){ForEach(this.filterPresets,(f:FilterPreset_wc1v,idx:number){Column({space:6}){Column().width(60).height(60).linearGradient({angle:135,colors:[[#FF6B9D,0],[#6C5CE7,1.0]]}).borderRadius(8).brightness(f.brightness).contrast(f.contrast).saturate(f.saturate).grayscale(f.grayscale).border({width:2,color:this.filterIndexidx?#1890ff:transparent})Text(f.name).fontSize(11).fontColor(this.filterIndexidx?#1890ff:#666666)}.alignItems(HorizontalAlign.Center).onClick((){this.filterIndexidx})})}.padding({left:16,right:16})}.scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off)}// 切割数量选择Column({space:12}){Text(切割数量).fontSize(14).fontWeight(FontWeight.Medium).fontColor(#1a1a1a).padding({left:16}).width(100%)Row({space:12}){ForEach([1,2,3,4,6,9],(n:number){Column({space:4}){// 预览小网格Column(){Grid(){ForEach(Array.from({length:n}),(_:Object,i:number){GridItem(){Column().width(100%).height(100%).backgroundColor(this.colorPalette[i%this.colorPalette.length])}})}.columnsTemplate(n1?1fr:n4?1fr 1fr:1fr 1fr 1fr).rowsGap(2).columnsGap(2).width(36).height(36)}.width(44).height(44).backgroundColor(this.selectedCountn?#e8f4ff:#f5f5f5).borderRadius(8).border({width:2,color:this.selectedCountn?#1890ff:transparent}).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)Text(${n}格).fontSize(11).fontColor(this.selectedCountn?#1890ff:#666666)}.alignItems(HorizontalAlign.Center).onClick((){this.selectedCountn})})}.justifyContent(FlexAlign.SpaceEvenly).width(100%)}// 间距设置Column({space:8}){Row(){Text(切割间距).fontSize(14).fontWeight(FontWeight.Medium).fontColor(#1a1a1a)Blank()Text(${this.spacing}px).fontSize(13).fontColor(#1890ff)}.padding({left:16,right:16}).width(100%)Slider({value:this.spacing,min:0,max:20,step:1}).width(90%).trackColor(#e8e8e8).selectedColor(#1890ff).onChange((v:number){this.spacingMath.round(v)})}// 切割预览Column({space:12}){Text(切割预览).fontSize(14).fontWeight(FontWeight.Medium).fontColor(#1a1a1a).padding({left:16}).width(100%)Grid(){ForEach(this.getCells(),(cell:GridCell,idx:number){GridItem(){Column().width(100%).height(100%).linearGradient({angle:135,colors:[[#FF6B9D,0],[#C44569,0.3],[#F8B500,0.6],[#6C5CE7,1.0]]}).brightness(this.currentFilter.brightness).contrast(this.currentFilter.contrast).saturate(this.currentFilter.saturate).grayscale(this.currentFilter.grayscale)}})}.columnsTemplate(this.cols1?1fr:this.cols2?1fr 1fr:1fr 1fr 1fr).rowsGap(this.spacing).columnsGap(this.spacing).width(100%).height(this.cols1?300:this.cols2?200:300).padding({left:16,right:16})}// 发布按钮Button(保存并发布).width(90%).height(50).fontSize(16).backgroundColor(#1890ff).borderRadius(25).fontWeight(FontWeight.Medium).margin({top:8})Column().height(32)}.padding({top:16})}.layoutWeight(1).backgroundColor(#f5f7fa)}.width(100%).height(100%).backgroundColor(#f5f7fa)}}一个小复盘这个案例可以当成一个小模板先把页面会变的东西放进状态再用函数或 Builder 处理重复逻辑最后让布局只关心展示。写 HarmonyOS7 页面时这个顺序通常比一上来堆 UI 更稳。