HarmonyOS ArkTS图片画廊深度解析:Grid 宫格布局、条件渲染与 Emoji 可视化

📅 2026/8/7 12:28:26
HarmonyOS ArkTS图片画廊深度解析:Grid 宫格布局、条件渲染与 Emoji 可视化
一、引言图片画廊是本次系列的收官之作也是布局最复杂、交互模式最丰富的应用。它核心展示了三个 ArkTS 进阶概念Grid 宫格布局使用columnsTemplate创建 3 列规则网格条件渲染块使用if条件渲染大图展示区全局选中态选中图片在高清展示区和网格缩略图中同步高亮此外它还创新地使用了Emoji 作为图片占位符——在纯代码项目中不需要真实图片资源也能营造出图片画廊的视觉体验。二、完整源码// index10.etsEntryComponentstruct Index10{Stateimages:string[][ 日出, 日落, 海洋, 雪山, 森林, 樱花, 荷花, 枫叶,❄️ 雪景, 彩虹, 夜景, 烟花];StateselectedIndex:number-1;build(){Column(){Text(图片画廊).fontSize(28).fontWeight(FontWeight.Bold).margin({top:40,bottom:10})// ── 大图展示区条件渲染──if(this.selectedIndex0){Column(){Text(this.images[this.selectedIndex]).fontSize(72).margin({top:20})Text(图片 (this.selectedIndex1) / this.images.length).fontSize(14).fontColor(#8E8E93).margin({top:10})Text(this.getImageName(this.images[this.selectedIndex])).fontSize(20).fontWeight(FontWeight.Bold).fontColor(#333333).margin({top:5})Row({space:20}){Button(◀ 上一张).height(40).fontSize(14).backgroundColor(#007AFF).borderRadius(20).enabled(this.selectedIndex0).onClick((){if(this.selectedIndex0)this.selectedIndex--;})Button(下一张 ▶).height(40).fontSize(14).backgroundColor(#007AFF).borderRadius(20).enabled(this.selectedIndexthis.images.length-1).onClick((){if(this.selectedIndexthis.images.length-1)this.selectedIndex;})}.margin({top:15})}.width(85%).backgroundColor(#F8F8F8).borderRadius(16).alignItems(HorizontalAlign.Center).padding({bottom:20})}Text(this.selectedIndex0?点击下方图片浏览:选择其他图片).fontSize(16).fontWeight(FontWeight.Bold).margin({top:this.selectedIndex0?10:20,bottom:10})// ── Grid 宫格 ──Grid(){ForEach(this.images,(item:string,index:number){GridItem(){Column(){Text(item.split( )[0]).fontSize(32).margin({top:15})Text(this.getImageName(item)).fontSize(12).fontColor(#666666).margin({bottom:10})}.width(100%).height(80).backgroundColor(this.selectedIndexindex?#E8F0FE:#F0F0F0).borderRadius(12).border({width:this.selectedIndexindex?2:0,color:#007AFF}).onClick((){this.selectedIndexindex;}).alignItems(HorizontalAlign.Center)}},(item:string)item)}.columnsTemplate(1fr 1fr 1fr).rowsGap(8).columnsGap(8).width(90%).layoutWeight(1)}.width(100%).height(100%).justifyContent(FlexAlign.Start).alignItems(HorizontalAlign.Center)}getImageName(emoji:string):string{returnemoji.substring(emoji.length-2);}}三、Grid 宫格布局组件3.1 Grid 构造函数Grid(){// 子组件必须是 GridItem}Grid是 ArkTS 中用于创建规则网格的容器。每个子项必须包裹在GridItem中。3.2 columnsTemplate 网格模板.columnsTemplate(1fr 1fr 1fr)columnsTemplate语法1fr 1fr 1fr → 3 列等宽 1fr 2fr 1fr → 中间列是两侧的 2 倍 100px 1fr 1fr → 第一列固定 100px后两列等分剩余 1fr 1fr 1fr 1fr → 4 列等宽 repeat(3, 1fr) → 等同 1fr 1fr 1frrepeat 语法fr单位fraction分数单位总可用宽度 容器宽度 ┌────────────── 90% 屏幕宽度 ──────────────┐ │ 1fr │ 1fr │ 1fr │ │ 33.3% │ 33.3% │ 33.3% │ └───────────┴──────────────┴──────────────┘如果columnsTemplate(1fr 2fr 1fr)│ 25% │ 50% │ 25% │3.3 行列间距.rowsGap(8)// 行间距 8vp.columnsGap(8)// 列间距 8vpGrid 的间距是设置在最外层的不像 Column/Row 要逐个子组件设置margin。3.4 GridItem 子组件ForEach(this.images,(item:string,index:number){GridItem(){Column(){Text(item.split( )[0])// Emoji.fontSize(32).margin({top:15})Text(this.getImageName(item))// 名称.fontSize(12).fontColor(#666666).margin({bottom:10})}.width(100%).height(80).backgroundColor(...).borderRadius(12).border(...).onClick((){this.selectedIndexindex;})}},(item:string)item)每个GridItem包含一个Column内部是 Emoji 图标 中文名称。四、Grid 与 List 的选择对比项GridList排列方向双向行列单向垂直/水平子组件GridItemListItem模板定义columnsTemplate无每项同宽滚动双向滚动单向滚动适用场景相册、应用桌面消息列表、设置页性能适合固定列数适合长列表选择原则数据需要多列展示 → Grid数据是列表形式 → List项大小不一致 → List每项可不同高度五、条件渲染if 块5.1 条件渲染展示大图build(){Column(){Text(图片画廊)...// 条件块selectedIndex 0 时显示大图if(this.selectedIndex0){Column(){Text(this.images[this.selectedIndex])// 大号 Emoji.fontSize(72)Text(图片 N / 12)// 页码Text(this.getImageName(...))// 图片名称Row(){Button(◀ 上一张)Button(下一张 ▶)}}// 圆角、背景、内边距...}// 提示文字根据是否有选中态显示不同内容Text(this.selectedIndex0?点击下方图片浏览:选择其他图片)// Grid 网格始终显示Grid(){...}}}ArkTS 中 if 块的规则规则说明位置只能在build()内部使用条件任意布尔表达式else支持if ... else ...嵌套不支持嵌套if用逻辑运算符替代与 JSX 区别不需要{包裹条件5.2 条件渲染的性能优势当selectedIndex -1时大图展示区的 Column 及其所有子组件完全不在组件树中// selectedIndex -1 时// 内存中没有这些组件// Text(72fp) ❌// Text(页码) ❌// Text(名称) ❌// Row(按钮) ❌// vs CSS 方案 visibility: hidden// 组件仍在内存只是不可见性能对比方案组件树内存可交互性if条件渲染不在树中不消耗不可交互visibility: hidden在树中消耗不可交互.opacity(0)在树中消耗仍可交互对于大图展示这种用时才显示的场景if条件渲染是最优选择。5.3 提示文字的联动Text(this.selectedIndex0?点击下方图片浏览:选择其他图片).margin({top:this.selectedIndex0?10:20,bottom:10})双联动一个表达式同时控制文本内容和上边距selectedIndex文本margin.top-1未选中“点击下方图片浏览”10vp≥ 0已选中“选择其他图片”20vp六、大图展示区的 UI 分析6.1 显示结构┌────────────────────────────────────────┐ │ │ │ 日出 │ ← Emoji (72fp) │ │ │ 图片 1 / 12 │ ← 页码 │ │ │ 日出 │ ← 名称 │ │ │ [◀ 上一张] [下一张 ▶] │ ← 导航按钮 │ │ └────────────────────────────────────────┘6.2 Emoji 作为图片替代Text(this.images[this.selectedIndex]).fontSize(72)在 72fp 字号下Emoji 占据约 64vp × 64vp 的显示区域相当于一张小型缩略图。这是纯代码项目中没有真实图片资源时的优雅替代方案。6.3 翻页导航Row({space:20}){Button(◀ 上一张).enabled(this.selectedIndex0).onClick((){if(this.selectedIndex0)this.selectedIndex--;})Button(下一张 ▶).enabled(this.selectedIndexthis.images.length-1).onClick((){if(this.selectedIndexthis.images.length-1)this.selectedIndex;})}首尾边界处理选中位置上一张按钮下一张按钮第一张index0❌ 禁用✅ 可用中间index1~10✅ 可用✅ 可用最后一张index11✅ 可用❌ 禁用七、Grid 项的选中高亮7.1 双指示器背景色 边框Column().backgroundColor(this.selectedIndexindex?#E8F0FE:#F0F0F0).border({width:this.selectedIndexindex?2:0,color:#007AFF})选中态 vs 非选中态属性非选中选中背景色#F0F0F0浅灰#E8F0FE浅蓝边框宽度0 (无)2vp 蓝色边框这种双指示器设计让选中项在视觉上非常醒目——边框是主指示器背景色是辅助指示器。7.2 State 的全局联动StateselectedIndex:number-1;当用户点击 GridItem 时点击 GridItem (index3) ↓ selectedIndex 3 ↓ ① 大图展示区出现if 条件变为 true → 渲染 Emoji、页码、名称、按钮 ② 大图展示区的 Emoji images[3] 雪山 ③ GridItem 的 backgroundColor 和 border 重新计算 → index3 的项变为蓝框高亮 → 其他项取消高亮 ④ 提示文字变为 选择其他图片一个状态变量驱动了 4 个 UI 区域的变化。八、字符串解析技巧8.1 Emoji 与名称的存储格式 日出 日落 海洋Emoji 和中文字符以空格分隔存储在同一个字符串中。8.2 split 提取 Emojiitem.split( )[0]// 日出 → [, 日出] → 8.3 substring 提取中文名getImageName(emoji:string):string{returnemoji.substring(emoji.length-2);}// 日出 → 取最后 2 字符 → 日出这种一个字符串存两个数据的技巧依赖于 Emoji 在字符串中占 2 个 UTF-16 单元长度 2空格 中文各占 1-2 个单元。 日出.length在 ArkTS 中返回5substring(3)取后两个字符得到日出。更通用的写法getImageName(emoji:string):string{returnemoji.substring(emoji.indexOf( )1);}// 找到空格的位置取之后的所有字符九、完整的交互状态映射状态大图区提示文字Grid 项翻页按钮初始(index-1)隐藏“点击下方图片浏览”全部非选中不存在选中第 1 张(index0)显示 日出“选择其他图片”第 1 项高亮上一张❌ 下一张✅选中第 6 张(index5)显示 樱花“选择其他图片”第 6 项高亮上一张✅ 下一张✅选中最后一张(index11)显示 烟花“选择其他图片”第 12 项高亮上一张✅ 下一张❌十、Grid 的布局技巧10.1 固定列数 自适应项Grid(){...}.columnsTemplate(1fr 1fr 1fr).width(90%)Grid 容器宽度为屏幕的 90%每列等分。项宽自适应(0.9 × 屏幕宽度) / 3 - 列间距。10.2 固定项高Column().width(100%).height(80)所有 GridItem 高度统一为 80vp保证网格整齐。10.3 自适应滚动当 Grid 内容超出容器高度时Grid 会自动滚动。.layoutWeight(1)使 Grid 占满Column的剩余空间。十一、扩展真实图片加载11.1 替换 Emoji 为 Image 组件interfaceGalleryImage{name:string;url:Resource;}Stateimages:GalleryImage[][{name:日出,url:$r(app.media.sunrise)},{name:日落,url:$r(app.media.sunset)},// ...];build(){GridItem(){Column(){Image(this.images[index].url).width(100%).height(60).objectFit(ImageFit.Cover)Text(this.images[index].name).fontSize(12).margin({bottom:6})}}}11.2 使用网络图片Image(https://example.com/images/sunrise.jpg).width(100%).height(60).objectFit(ImageFit.Cover).alt($r(app.media.placeholder))// 加载中占位图十二、总结通过图片画廊应用我们深入掌握了知识点重要程度关键代码Grid宫格布局必须掌握Grid() { GridItem() {...} }columnsTemplate列模板核心技能.columnsTemplate(1fr 1fr 1fr)if条件渲染进阶技能if (condition) { Column() {...} }selectedIndex全局选中态常用模式一个State驱动多区域 UI翻页边界控制用户体验enabled(index 0)控制按钮Emoji 作为 UI 元素创意技巧Text().fontSize(72)字符串解析split/substring基础技能item.split( )[0]双指示器选中效果视觉设计背景色 边框联合变化图片画廊是本次 11 个迷你应用系列的收官之作它综合运用了State、Grid、条件渲染、ForEach、Button等所有前序应用中出现过的知识点并在此基础上展示了条件块渲染和 Grid 布局。系列总结至此我们完成了全部11 篇深度博客文章从 Hello World 到图片画廊覆盖了 ArkTS 开发的完整知识体系应用核心知识点难度Index — Hello WorldEntryComponentStateRelativeContainer$r()⭐index1 — 计数器State响应式、Column/Row、Button样式⭐index2 — 待办事项TextInput绑定、ForEach列表渲染、CRUD⭐⭐index3 — 计时器setInterval、生命周期、enabled条件禁用⭐⭐index4 — 计算器Builder复用、switch运算、状态机⭐⭐⭐index5 — 随机数parseInt/isNaN验证、Math.random、FIFO⭐⭐index6 — 颜色选择器Slider滑块、RGB↔Hex 转换、Builder参数化⭐⭐⭐index7 — 备忘录完整 CRUD、编辑/新增双模式、TextArea⭐⭐⭐index8 — 单位转换器Select下拉、中间基准换算算法、toFixed⭐⭐⭐⭐index9 — 天气卡片热力图背景色、单一状态驱动、emoj装饰⭐⭐⭐index10 — 图片画廊Grid宫格、if条件渲染、翻页控制⭐⭐⭐⭐Happy coding on HarmonyOS!