前言
在内容型应用中,视图切换是提升用户体验的重要功能。用户可以根据自己的偏好选择网格浏览或列表浏览。“海风日记“的首页支持双栏网格(Grid)和单栏列表(List)两种视图模式,通过右上角的切换按钮实现一键切换。
本文将从HomeTab.ets源码出发,深入讲解 Grid 和 List 组件的完整配置、视图切换的实现机制以及数据驱动渲染的最佳实践。
一、Grid 与 List 组件概述
1.1 Grid 组件
Grid 用于实现网格布局,支持自定义列数、行数和间距:
Grid() { ForEach(this.diaryList, (item: DiaryEntry) => { GridItem() { DiaryCard({ ... }) } }) } .columnsTemplate('1fr 1fr') // 双栏 .columnsGap(10) // 列间距 .rowsGap(10) // 行间距 .padding({ left: 12, right: 12 }) .layoutWeight(1)1.2 List 组件
List 用于实现列表布局,支持垂直或水平滚动:
List({ space: 12 }) { ForEach(this.diaryList, (item: DiaryEntry) => { ListItem() { DiaryCard({ ... }) } }) } .padding({ left: 14, right: 14 }) .layoutWeight(1) .scrollBar(BarState.Off)1.3 Grid 与 List 的对比
| 对比维度 | Grid | List |
|---|---|---|
| 布局方式 | 网格 | 单列 |
| 显示密度 | 高(一屏可展示更多内容) | 低 |
| 适用场景 | 图片为主、快速浏览 | 文字为主、详细阅读 |
| 内容展示 | 单行显示更多信息 | 信息更完整 |
二、视图切换的实现
2.1 状态变量
@Component export struct HomeTab { @State isGridView: boolean = true // 当前视图模式 @State diaryList: DiaryEntry[] = MOCK_DIARIES // 日记数据 }2.2 切换按钮
Button() { SymbolGlyph( this.isGridView ? $r('sys.symbol.square_grid_2x2') // 网格图标 : $r('sys.symbol.list_bullet') // 列表图标 ) .fontSize(18) .fontColor(['#C8956A']) } .width(30).height(30) .borderRadius(8) .backgroundColor('rgba(245,166,35,0.10)') .onClick(() => { this.isGridView = !this.isGridView })2.3 条件渲染视图
if (this.isGridView) { // 双栏网格 Grid() { ForEach(this.diaryList, (item: DiaryEntry) => { GridItem() { DiaryCard({ ... }) } }) } .columnsTemplate('1fr 1fr') .columnsGap(10).rowsGap(10) .padding({ left: 12, right: 12, bottom: TAB_BAR_HEIGHT + 16 }) .layoutWeight(1) } else { // 单栏列表 List({ space: 12 }) { ForEach(this.diaryList, (item: DiaryEntry) => { ListItem() { DiaryCard({ ... }) } }) } .padding({ left: 14, right: 14, bottom: TAB_BAR_HEIGHT + 16 }) .layoutWeight(1) .scrollBar(BarState.Off) }三、Grid 组件的属性详解
3.1 columnsTemplate
.columnsTemplate('1fr 1fr') // 两列,每列等宽columnsTemplate使用 fr 单位来分配列宽:
| 模板 | 效果 |
|---|---|
'1fr 1fr' | 两列等宽 |
'1fr 2fr' | 第二列宽度是第一列的两倍 |
'repeat(3, 1fr)' | 三列等宽 |
3.2 间距控制
.columnsGap(10) // 列间距 10vp .rowsGap(10) // 行间距 10vp3.3 滚动控制
Grid 默认可滚动,无需额外配置。如果需要禁用滚动:
Grid() .scrollBar(BarState.Off) // 隐藏滚动条四、List 组件的属性详解
4.1 间距
List({ space: 12 }) // 列表项间距 12vp4.2 滚动条
List() .scrollBar(BarState.Off) // 隐藏滚动条 // 或 .scrollBar(BarState.Auto) // 自动显示 // 或 .scrollBar(BarState.On) // 始终显示4.3 列表项
List() { ForEach(this.diaryList, (item: DiaryEntry) => { ListItem() { DiaryCard({ ... }) } }) }五、视图切换的动画效果
5.1 使用 animateTo
.onClick(() => { animateTo({ duration: 300 }, () => { this.isGridView = !this.isGridView }) })5.2 过渡动画
// 视图切换时添加过渡动画 Grid() .transition({ type: TransitionType.Insert, scale: { x: 0, y: 0 } }) .transition({ type: TransitionType.Delete, scale: { x: 0, y: 0 } })六、数据驱动渲染
6.1 ForEach 的使用
ForEach( this.diaryList, // 数据源 (item: DiaryEntry) => { // UI 构建函数 GridItem() { DiaryCard({ ... }) } }, (item: DiaryEntry) => item.id // 键值生成函数 )6.2 键值的重要性
键值用于标识列表项的唯一性,帮助 ArkUI 高效更新列表:
- 键值必须是唯一的
- 键值不建议使用数组索引
- 建议使用数据 ID 作为键值
七、常见问题与排查
7.1 Grid 显示空白
问题:Grid 区域显示为空白。
原因:Grid 没有设置columnsTemplate,或数据源为空。
解决方案:
Grid() .columnsTemplate('1fr 1fr') // 必须设置列模板7.2 List 滚动条不消失
问题:设置了scrollBar(BarState.Off)但滚动条仍然显示。
原因:属性设置顺序错误。
解决方案:确保scrollBar在布局属性之后设置。
总结
本文通过“海风日记“首页的源码,深入讲解了 Grid 和 List 组件的完整用法:
- Grid 组件:双栏网格布局的配置
- List 组件:单栏列表布局的配置
- 视图切换:@State + 条件渲染实现一键切换
- 属性详解:columnsTemplate、间距、滚动条
- 动画效果:animateTo 和过渡动画
- 数据驱动:ForEach 的数据绑定和键值管理
下一篇文章将深入讲解ForEach 数据驱动渲染,敬请期待。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- Grid 组件文档
- List 组件文档
- ForEach 文档
- 海风日记项目源码
- [HarmonyOS 开发者官网](https://atomgit.com/openharmony/docs
- 开源鸿蒙跨平台社区
- ArkUI 动画概述
- animateTo 动画