【共创季稿事节】鸿蒙原生 ArkTS 布局实现 Column + List + Navigation 协作导航 — 从列表渲染到页面切换的完整实践



目录
前言
三大核心组件概述
2.1 Column —— 弹性列布局
2.2 List —— 虚拟滚动列表
2.3 Navigation —— 页面导航容器
列表—导航协作模式的设计思想
3.1 为什么需要协作布局
3.2 数据驱动 vs DOM 操作
3.3 状态驱动的页面切换
项目搭建与配置
4.1 工程结构总览
4.2 主题色系统设计
4.3 数据类型定义
Index.ets 完整代码逐段解析
5.1 数据组织:列表数据源设计
5.2 状态体系:@State currentDetail 驱动页面切换
5.3 列表页(首页)的 Column 分层结构
5.4 标题区域——品牌呈现
5.5 搜索操作栏——Row 水平布局
5.6 List 虚拟滚动列表——核心渲染区域
5.7 ListItem 内容模板——图标 + 标题 + 箭头
5.8 详情页的 Scroll + Column 双布局组合
5.9 详情内容区的多层次 Column 嵌套
5.10 Navigation 容器属性详解
Navigation 导航模式深度解析
6.1 Navigation 工作原理
6.2 标题模式:Mini vs Full
6.3 返回按钮的联动控制
6.4 NavRouter 与条件渲染的比较
List 虚拟滚动机制详解
7.1 虚拟滚动的核心原理
7.2 缓存策略:cachedCount
7.3 性能对比:List vs Scroll + Column
Column 布局深入实践
8.1 Column 的嵌套策略
8.2 layoutWeight 的空间分配
8.3 Column + Row 的组合模式
编译与调试经验
9.1 List 子组件限制
9.2 NavRouter 废弃的迁移路线
9.3 NavigationTitleMode 类型约束
效果展示与交互流程
10.1 列表首页布局
10.2 详情页布局
10.3 典型交互流程
性能分析与最佳实践
11.1 列表渲染性能
11.2 最佳实践清单
扩展方向
12.1 搜索过滤功能
12.2 分组列表——Section 组件
12.3 路由传参与多级导航
总结
- 前言
在移动应用开发中,「列表页 → 详情页」 是最经典、最高频的页面模式之一。无论是新闻客户端、电商应用、社交平台还是企业工具,几乎每一个应用都包含「条目列表 + 点击查看详情」的交互流程。
在 HarmonyOS NEXT 的 ArkUI 框架中,这一模式可以通过 Column + List + Navigation 三个核心组件的协作来优雅实现:
Column 负责垂直布局的组织——标题、搜索栏、列表内容、底部状态,全部由 Column 组装
List 负责高性能列表渲染——虚拟滚动机制确保即使是上万条数据也不卡顿
Navigation 负责页面导航管理——标题栏、返回按钮、页面切换动画一站式解决
本文以一份完整的 ArkTS 代码项目为载体,从 数据组织、状态管理、布局编排、导航控制 四个维度深入解析这一协作模式。项目本身也是一个「自指」的知识点集合——8 个列表条目本身就是 8 篇 ArkUI 核心知识点的介绍,点击后可查看详细说明。
无论你正在开发第一个鸿蒙应用,还是在寻找更优雅的页面组织方式,本文都将提供从理论到实践的全面参考。
- 三大核心组件概述
2.1 Column —— 弹性列布局
Column 是 ArkUI 中最基础、最常用的布局容器。它的核心语义很简单:子组件从上到下依次排列。
Column
├── 子组件 A ← top
├── 子组件 B
├── 子组件 C
└── 子组件 D ← bottom
但 Column 的强大之处在于它丰富的对齐和空间分配能力:
属性 作用 常用值
justifyContent(FlexAlign) 主轴(垂直)方向对齐 Start(默认)、Center、End、SpaceBetween、SpaceAround
alignItems(HorizontalAlign) 交叉轴(水平)方向对齐 Start、Center(默认)、End、Stretch
layoutWeight(number) 子组件在主轴上的权重分配 正数,比例决定剩余空间的分配比例
width(‘100%’) 宽度设置 百分比、vp 数值、LengthMode
padding() / margin() 内边距 / 外边距 四边统一或分别指定
在本项目中的角色: Column 是页面结构的"骨架"——它把标题区、搜索栏、列表区、底部提示垂直组合在一起,每一部分本身又可能是 Column 或 Row 的嵌套。
2.2 List —— 虚拟滚动列表
List 是 ArkUI 专门为 长列表渲染 设计的高性能组件。它的最大特点是 虚拟滚动(Virtual Scrolling):
只渲染当前可见区域内的 ListItem
回收复用离屏的 ListItem
预缓存前后若干项(通过 cachedCount 控制)
屏幕可见区域
┌──────────────────────┐
│ ListItem A (可见) │ ← 渲染
│ ListItem B (可见) │ ← 渲染
│ ListItem C (可见) │ ← 渲染
├──────────────────────┤
│ ListItem D (离屏) │ ← 回收,不复存在
│ ListItem E (离屏) │ ← 回收
│ ListItem F (离屏) │ ← 回收
│ … (还有 9994 项) │ ← 从未存在
└──────────────────────┘
核心属性:
属性 作用 本项目的取值
space(number) 列表项间距 8 vp
listDirection(Axis) 列表方向 Axis.Vertical
scrollBar(BarState) 滚动条策略 BarState.Auto(自动显示/隐藏)
cachedCount(number) 预缓存项数 3
divider(DividerStyle) 分割线样式 未使用(通过 space 控制间距)
重要限制: List 的直接子组件只能是 ListItem、ListItemGroup 或 Section。任何其他组件(如 Row、Column、NavRouter)都不能直接放在 List 下面。
2.3 Navigation —— 页面导航容器
Navigation 是 ArkUI 为 多页面应用 设计的导航容器。它自动管理:
标题栏:显示当前页面标题
返回按钮:可在详情页自动显示,首页自动隐藏
页面切换动画:默认提供堆栈式 Push/Pop 动画
标题模式:支持 Mini(紧凑)和 Full(标准)两种
Navigation 容器结构
┌─────────────────────────┐
│ ← 返回 标题文字 │ ← 自动管理的标题栏
├─────────────────────────┤
│ │
│ 页面内容(根据状态切换) │
│ │
└─────────────────────────┘
本项目使用的属性:
Navigation()
.title(‘ArkTS 布局导航’)
.titleMode(NavigationTitleMode.Mini)
.hideBackButton(this.currentDetail === null)
.hideToolBar(true)
.mode(NavigationMode.Stack)
3. 列表—导航协作模式的设计思想
3.1 为什么需要协作布局
如果只用单一组件实现「列表 + 详情」,会面临以下问题:
单一组件方案 问题
只用 Column 长列表会渲染全部子组件 → 性能差;无导航管理
只用 List 只能渲染列表,不能自主实现页面级切换
只用 Navigation 没有内置的列表渲染能力,需要额外组合
协作布局的优势:
Column 负责"垂直拼装" → 页面各区域的布局
List 负责"高效渲染" → 列表数据的虚拟滚动
Navigation 负责"页面管理" → 标题栏 + 返回按钮 + 页面切换
三者各司其职,组合起来就构成了一个完整的「列表浏览 → 详情查看」功能单元。
3.2 数据驱动 vs DOM 操作
在传统的命令式 UI 开发中,开发者需要手动操作 DOM:
// 传统方式(伪代码)
const list = document.querySelector(‘#list’);
list.innerHTML = items.map(item => <li>${item.title}</li>);
list.addEventListener(‘click’, (e) => {
const detail = document.querySelector(‘#detail’);
detail.innerHTML = <h1>${e.target.title}</h1><p>${e.target.content}</p>;
detail.style.display = ‘block’;
});
而在 ArkTS 的声明式 UI 中:
// 声明式方式
build() {
List() {
ForEach(this.listData, (item) => {
ListItem() {
Text(item.title)
}
.onClick(() => { this.currentDetail = item; });
});
}
// 当 currentDetail 变化时,UI 自动切换
}
核心区别: 开发者只描述"数据与 UI 的映射关系",框架自动处理渲染和更新。
3.3 状态驱动的页面切换
本项目的页面切换完全由 @State currentDetail 这一个状态变量驱动:
currentDetail = null → 渲染列表首页
currentDetail = item → 渲染 item 的详情页
currentDetail = null (返回) → 重新渲染列表首页
状态转换图:
点击列表项
┌─────────────────────────────────┐
│ │
▼ │
┌──────────┐ ┌──────────────┐
│ 列表首页 │ │ 详情页 │
│ current │── 点击条目 ──→│ current │
│ Detail │ │ Detail │
│ = null │←── 返回 ────│ = item │
└──────────┘ └──────────────┘
这种模式的好处:
单一数据源:页面状态完全由 currentDetail 表达,没有冗余状态
可预测:给定 currentDetail 的值,UI 是确定的
可回溯:返回首页只需将 currentDetail 置 null
易于调试:在 DevEco Studio 中直接查看 @State 的值即可了解当前页面状态
4. 项目搭建与配置
4.1 工程结构总览
Demo06302/
├── build-profile.json5 ← targetSdk=6.1.1.24 (API 12)
├── AppScope/
│ └── app.json5 ← bundleName: com.example.demo0630
└── entry/
├── build-profile.json5 ← apiType: stageMode
├── src/
│ └── main/
│ ├── module.json5 ← 注册 EntryAbility + 页面路由
│ ├── resources/ ← element / media / profile
│ └── ets/
│ ├── entryability/
│ │ └── EntryAbility.ets ← 加载 pages/Index
│ └── pages/
│ └── Index.ets ← 核心实现 (459行)
└── oh-package.json5
4.2 主题色系统设计
本项目采用 Indigo(靛蓝) 作为主色调,营造沉稳、专业、学习的氛围:
用途 色值 色名 应用位置
主标题 #1A237E Indigo 900 页面大标题、详情页标题
次标题 #283593 Indigo 800 详情区子标题
强调色 #5C6BC0 Indigo 400 搜索按钮、返回按钮
辅助色 #9FA8DA Indigo 200 说明文字、底部状态
标题背景 #E8EAF6 Indigo 50 标题区背景色
列表项 #FFFFFF 纯白 ListItem 背景
页面底 #F5F5F5 Grey 100 整体页面背景
警告色 #FFF8E1 Amber 50 原理说明卡片背景
警告文字 #E65100 Deep Orange 900 原理说明文字颜色
色彩选择背后的考量:
蓝色系给人冷静、专注的感觉,适合"学习"场景
高对比度(标题 900 vs 背景 50)确保信息层次清晰
白色列表项在浅灰背景上形成"卡片"效果,视觉分区明显
橙色提示区与蓝色主调形成互补色对比,吸引注意力
4.3 数据类型定义
interface ListItemData {
id: number; // 唯一标识
title: string; // 主标题(列表首页显示)
subtitle: string; // 副标题(列表首页显示)
icon: string; // 图标(emoji,装饰性标识)
detailTitle: string; // 详情页标题
detailContent: string; // 详情页内容(支持 \n 换行)
}
数据结构设计要点:
字段 为什么需要 在何处使用
id 作为唯一标识,未来可用于搜索过滤 详情页元信息
title 列表项的主标题,用户最关注的信息 列表首页的 ListItem
subtitle 补充说明,降低阅读决策成本 列表首页的 ListItem
icon 视觉装饰,增强辨识度 列表首页的 ListItem + 详情页头部
detailTitle 详情页标题,与列表标题可不同 Navigation 标题栏 + 详情页头部
detailContent 核心内容,富文本格式 详情页内容区
将 title 和 detailTitle 分开设计的理由是:列表标题通常需要简短易扫描(如"List 虚拟滚动"),而详情页标题可以更完整(如"List 虚拟滚动列表"),两者的信息密度需求不同。
- Index.ets 完整代码逐段解析
5.1 数据组织:列表数据源设计
private readonly listData: ListItemData[] = [
{
id: 1,
title: ‘鸿蒙原生开发’,
subtitle: ‘ArkTS 语言特性与声明式 UI’,
icon: ‘📱’,
detailTitle: ‘鸿蒙原生开发概述’,
detailContent: ‘ArkTS 是鸿蒙原生应用的首选开发语言…’
},
// … 共 8 条
];
设计意图: 8 条知识点本身就是一篇"微型 ArkUI 教程"。用户在使用应用的过程中,不仅看到了布局效果,还能学到以下知识点:
ID 知识点 学习要点
1 鸿蒙原生开发 ArkTS 核心特性概览
2 Column 布局 对齐、权重、嵌套
3 List 虚拟滚动 缓存、方向、虚拟化原理
4 Navigation 导航 页面栈、标题栏、返回控制
5 Scroll 滚动容器 与 List 的对比
6 @State 状态管理 响应式编程核心
7 ForEach 循环渲染 声明式列表映射
8 Row 水平布局 水平排列与 Flex 对齐
这种"应用即教程"的设计让示例代码本身就具有教学价值。
5.2 状态体系:@State currentDetail 驱动页面切换
@State currentDetail: ListItemData | null = null;
@State searchValue: string = ‘’;
两个 @State 变量是整个应用的"大脑":
变量 类型 初始值 作用
currentDetail ListItemData null null
searchValue string ‘’ 演示交互,存储搜索输入
currentDetail 的三态逻辑:
// build() 中的条件判断
if (this.currentDetail === null) {
// → 渲染列表首页
} else {
// → 渲染选中条目的详情页
}
// 导航栏返回按钮联动
.hideBackButton(this.currentDetail === null)
// → 首页隐藏,详情页显示
这种模式是 ArkTS 中实现"页面切换"的标准方式——不需要复杂的路由配置,一个 @State 变量 + 一个 if/else 足矣。
5.3 列表页(首页)的 Column 分层结构
列表页的布局完全依靠 Column 的嵌套实现:
Column (page-root, height=100%, padding=8)
│
├── Column (标题区, backgroundColor=#E8EAF6)
│ ├── Text(“📖 ArkTS 布局导航”)
│ ├── Text(“Column + List + Navigation 协作布局”)
│ └── Text(“点击列表项查看详情…”)
│
├── Row (搜索/操作栏, width=95%)
│ ├── TextInput(placeholder=“🔍 搜索…”)
│ └── Button(“⇅ 排序”)
│
├── Row (列表计数, “📚 共 8 个知识点”)
│
├── List (虚拟滚动列表, layoutWeight=1 ← 占满剩余空间)
│ └── ForEach × 8 条
│ └── ListItem → Row(图标 + 标题Column + 箭头)
│
└── Row (底部提示, “💡 点击任意条目查看详情 →”)
关键点: layoutWeight(1) 让 List 占满 Column 中标题区、搜索栏、计数行和底部提示之外的所有剩余空间,确保列表有最大化的展示区域。
5.4 标题区域——品牌呈现
Column() {
Text(‘📖 ArkTS 布局导航’)
.fontSize(22)
.fontWeight(FontWeight.Bold)
.fontColor(‘#1A237E’)
.margin({ bottom: 4 });
Text(‘Column + List + Navigation 协作布局’)
.fontSize(13)
.fontColor(‘#5C6BC0’)
.margin({ bottom: 6 });
Text(‘点击列表项查看详情,Navigation 自动管理页面切换’)
.fontSize(11)
.fontColor(‘#9FA8DA’)
.textAlign(TextAlign.Center);
}
.width(‘100%’)
.padding({ top: 16, bottom: 12 })
.backgroundColor(‘#E8EAF6’)
.borderRadius({ bottomLeft: 16, bottomRight: 16 });
设计细节:
三级标题体系:主标题(22fp)→ 副标题(13fp)→ 说明(11fp),逐级缩小字号
仅底部圆角(bottomLeft/bottomRight)而非全部圆角,形成「顶部悬挂」效果
背景色 #E8EAF6 与页面主体 #F5F5F5 形成微妙色差,区分区域
5.5 搜索操作栏——Row 水平布局
Row() {
TextInput({ placeholder: ‘🔍 搜索布局知识…’ })
.layoutWeight(1)
.height(36)
.fontSize(13)
.backgroundColor(‘#FFFFFF’)
.borderRadius(18)
.padding({ left: 16, right: 16 })
.onChange((val: string) => {
this.searchValue = val;
});
Button() {
Text(‘⇅ 排序’)
.fontSize(12)
.fontColor(‘#FFFFFF’);
}
.type(ButtonType.Capsule)
.height(36)
.backgroundColor(‘#5C6BC0’)
.margin({ left: 8 });
}
.width(‘95%’)
.padding({ top: 8, bottom: 8 })
.alignItems(VerticalAlign.Center);
布局要点:
layoutWeight(1) 让 TextInput 占据按钮之外的全部水平空间
输入框使用 borderRadius(18) 实现圆角效果(高度 36,一半即为 18,形成完美的胶囊形)
按钮使用 ButtonType.Capsule 同样保持胶囊形,视觉风格统一
alignItems(VerticalAlign.Center) 确保两个子组件垂直居中
5.6 List 虚拟滚动列表——核心渲染区域
List({ space: 8 }) {
ForEach(this.listData, (item: ListItemData) => {
ListItem() {
// … 列表项内容
}
.width(‘100%’)
.backgroundColor(‘#FFFFFF’)
.borderRadius(12)
.onClick(() => {
this.onItemClick(item);
});
});
}
.width(‘100%’)
.layoutWeight(1)
.listDirection(Axis.Vertical)
.scrollBar(BarState.Auto)
.cachedCount(3)
.margin({ top: 4 });
参数解析:
参数 值 作用
space: 8 8vp 列表项之间的垂直间距
layoutWeight(1) 1 占据 Column 中剩余的所有空间
listDirection(Axis.Vertical) 纵向 标准的从上到下列表
scrollBar(BarState.Auto) 自动 滚动时显示滚动条,静止时隐藏
cachedCount(3) 3 在可见区域外,上下各保留 3 个渲染的 Item
5.7 ListItem 内容模板——图标 + 标题 + 箭头
ListItem() {
Row() {
// 1. 图标(左)
Text(item.icon).fontSize(28).margin({ right: 12 });
// 2. 标题 + 副标题(中)
Column() {
Text(item.title)
.fontSize(16).fontWeight(FontWeight.Medium).fontColor('#37474F');
Text(item.subtitle)
.fontSize(12).fontColor('#90A4AE').margin({ top: 2 });
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start);
// 3. 右箭头指示器(右)
Text('›').fontSize(22).fontColor('#B0BEC5');
}
.width(‘100%’)
.padding(12)
.alignItems(VerticalAlign.Center);
}
这是移动端最经典的「左图标 + 中标题 + 右箭头」模式,在各种应用中广泛使用。
布局分析:
┌──────────────────────────────────────────┐
│ ┌────┐ ┌──────────────────┐ ┌────────┐ │
│ │ 📱 │ │ 鸿蒙原生开发 │ │ › │ │
│ │ │ │ ArkTS 语言特性… │ │ │ │
│ └────┘ └──────────────────┘ └────────┘ │
│ 图标 标题 + 副标题 右箭头 │
│ (28fp) (layoutWeight=1) (22fp) │
└──────────────────────────────────────────┘
图标列固定宽度(由内容撑开)
中间列使用 layoutWeight(1) 占据所有弹性空间
右箭头列固定宽度
这种模式保证了在任意屏幕宽度下,标题都能获得最大展示空间。
5.8 详情页的 Scroll + Column 双布局组合
Column() {
Scroll() {
Column() {
// 标题区
// 内容区
// 按钮区
// 原理说明区
}
.width(‘100%’)
.padding(12);
}
.width(‘100%’)
.layoutWeight(1);
}
为什么详情页不使用 List 而使用 Scroll + Column?
对比维度 Scroll + Column List
子组件类型 任意组件 仅限 ListItem/ListItemGroup/Section
渲染策略 全部渲染 虚拟滚动(按需渲染)
适用场景 内容形式多样的页面 大量同构列表项
布局灵活性 极高(可嵌套各种组件) 低(结构受限)
详情页包含标题区(Column)、内容卡片(Column)、按钮行(Row)、说明卡片(Column)等形式各异的内容区块,因此 Scroll + Column 是比 List 更合适的选择。
5.9 详情内容区的多层次 Column 嵌套
详情页内容区展示了 Column 的 多层嵌套 能力:
Scroll
└── Column (root, padding=12)
│
├── Column (标题区, 圆角16)
│ ├── Text(图标, fontSize=48)
│ ├── Text(detailTitle, fontSize=22, Bold)
│ └── Text(元信息行, fontSize=11)
│
├── Column (内容卡片, 白色背景, 圆角12)
│ ├── Text(“📝 详细介绍”, fontSize=16)
│ └── Text(detailContent, fontSize=14)
│
├── Row (按钮组)
│ ├── Button(“← 返回列表”, 靛蓝)
│ └── Button(“📌 收藏”, 浅靛蓝)
│
└── Column (原理说明, 橙色背景, 圆角12)
├── Text(“💡 Column + List + Navigation 协作原理”)
└── Text(“• Column:…\n• List:…\n• Navigation:…”)
每一层 Column 都独立设置 width(‘100%’),确保在水平方向撑满。
5.10 Navigation 容器属性详解
Navigation()
.title(‘ArkTS 布局导航’)
.titleMode(NavigationTitleMode.Mini)
.hideBackButton(this.currentDetail === null)
.hideToolBar(true)
.mode(NavigationMode.Stack)
属性 值 效果
title(‘ArkTS 布局导航’) 字符串 设置导航栏标题
titleMode(Mini) 迷你模式 标题栏占用高度更小,留给内容更多空间
hideBackButton(动态) 布尔值 currentDetail === null 时隐藏,否则显示
hideToolBar(true) 永久隐藏 不显示底部工具栏
mode(Stack) 堆栈模式 页面以堆栈方式切换(Push/Pop)
hideBackButton 的动态联动:
.hideBackButton(this.currentDetail === null)
首页:currentDetail === null → hideBackButton(true) → 隐藏返回按钮
详情页:currentDetail !== null → hideBackButton(false) → 显示返回按钮
不需要手动监听返回按钮的点击事件——Navigation 在用户点击返回按钮时会触发一个"系统级"的返回动作。但由于本项目的页面切换是通过 @State 实现的,返回按钮的点击行为需要额外处理。
在 HarmonyOS NEXT 中,Navigation 的返回按钮点击默认会触发页面栈的 pop 操作。对于使用 @State 条件渲染实现的页面切换,需要通过 onBackClick 回调监听:.onBackClick(() => { this.onBackToList(); return false; })。不过在本版本的 API 中,由于我们使用了 if/else 条件渲染而非真正的 NavDestination 页面栈,返回按钮的显示/隐藏联动本身已经提供了足够的交互提示。
- Navigation 导航模式深度解析
6.1 Navigation 工作原理
Navigation 在底层维护了一个 页面栈(Page Stack):
操作:用户点击列表项
→ 推入详情页(Push)
┌──────┐
│ 详情页 │ ← Top
├──────┤
│ 首页 │
└──────┘
操作:用户点击返回
→ 弹出详情页(Pop)
┌──────┐
│ 首页 │ ← Top
└──────┘
在条件渲染模式下(本项目使用),虽然没有真正的页面栈,但 Navigation 仍然维持了标题栏和返回按钮的一致性。
6.2 标题模式:Mini vs Full
Navigation 提供两种标题模式:
模式 标题栏高度 适用场景 本项目选择原因
NavigationTitleMode.Mini 约 40vp 内容密集型页面 列表内容更重要
NavigationTitleMode.Full 约 80vp 品牌展示型页面 适合需要大标题的应用
Mini 模式在移动设备上能多展示 1~2 个列表项,对于列表浏览型应用来说收益明显。
6.3 返回按钮的联动控制
hideBackButton 的巧妙之处在于它绑定了一个动态的 @State 表达式:
.hideBackButton(this.currentDetail === null)
当 @State 变化时,Navigation 自动重新计算属性值,无需任何额外代码。这种"响应式属性绑定"是 ArkUI 声明式 UI 的核心优势。
6.4 NavRouter 与条件渲染的比较
在开发初期,我尝试使用 NavRouter 组件来实现导航,但遇到了两个问题:
问题 说明
List 子组件限制 List 的直接子组件只能是 ListItem,而 NavRouter 包裹 ListItem 违反了约束
NavRouter 废弃 在 API 12 中,NavRouter 已被标记废弃
迁移方案: 使用 @State 条件渲染代替 NavRouter。
NavRouter 方案(旧) @State 方案(新)
Navigation() Navigation()
├── List() ├── if (state === null)
│ └── NavRouter() │ → 列表首页
│ └── ListItem() └── else
└── NavDestination() → 详情页
新方案的优势:
更简单:不需要学习和配置路由组件
更灵活:页面内容完全由代码控制
更高效:避免了不必要的组件层级
7. List 虚拟滚动机制详解
7.1 虚拟滚动的核心原理
传统列表(如 Scroll + ForEach)会渲染所有列表项的 UI 组件:
Scroll 传统模式(渲染全部)
┌────────────┐
│ Item 1 │ ← 渲染
│ Item 2 │ ← 渲染
│ Item 3 │ ← 渲染
│ … │ ← 渲染(全部 10000 项)
│ Item 9999 │ ← 渲染
│ Item 10000 │ ← 渲染
└────────────┘
→ 内存占用:10000 个组件的开销
→ 性能:大量 DOM 节点,滑动卡顿
而 List 的虚拟滚动模式只渲染可视区域附近的几个项:
List 虚拟滚动(按需渲染)
┌────────────┐
│ Item 1 │ ← 回收(不可见)
│ Item 2 │ ← 缓存 (cachedCount)
│────────────│
│ Item 3 │ ← 渲染(可见)
│ Item 4 │ ← 渲染(可见)
│ Item 5 │ ← 渲染(可见)
│────────────│
│ Item 6 │ ← 缓存 (cachedCount)
│ Item 7 │ ← 未创建
│ … │ ← 未创建
└────────────┘
→ 内存占用:7 个组件(3 可见 + 2+2 缓存)
7.2 缓存策略:cachedCount
cachedCount 控制可见区域之外预渲染的列表项数量:
cachedCount 值 总渲染项数 滑动体验 内存占用
0 3 项(仅可见) 快速滑动可能出现白屏 最低
3 9 项(3可见+3上+3下) 滑动流畅 低
10 23 项 极速滑动也无延迟 中等
100 203 项 无需更多缓存 较高
本项目设置为 cachedCount(3),兼顾了流畅度和内存占用。
7.3 性能对比:List vs Scroll + Column
场景 List(虚拟滚动) Scroll + Column
10 条数据 ✅ 优秀 ✅ 优秀
100 条数据 ✅ 优秀 ✅ 良好
1000 条数据 ✅ 优秀 ❌ 明显卡顿
10000 条数据 ✅ 流畅(仅渲染~9项) ❌ 不可用
混合内容布局 ❌ 受限(只能 ListItem) ✅ 完全自由
黄金法则: 列表项数量超过 50 条时,优先选择 List;50 条以下且内容形式多样时,Scroll + Column 更灵活。
- Column 布局深入实践
8.1 Column 的嵌套策略
在整个应用中,Column 以 4 层嵌套组织页面:
Level 0: Navigation (根容器)
Level 1: Column (页面根容器,含 padding)
Level 2: Column (标题区) / List (列表区) / Row (底部)
Level 3: Column (详情内容卡片) / Row (列表项)
Level 4: Column (标题文字组) / Row (按钮组)
嵌套规则:
每层 Column 负责一个明确的视觉区块
避免超过 5 层嵌套(超出后代码可读性下降)
每个 Column 都设置 width(‘100%’),确保水平撑满
8.2 layoutWeight 的空间分配
layoutWeight 是 Column 和 Row 中最强大的空间分配工具:
Column() {
// 标题区 - 不设 layoutWeight → 由内容撑开(固定高度)
Text(‘标题’).fontSize(22);
// 列表区 - layoutWeight(1) → 占满所有剩余空间
List() { }.layoutWeight(1);
// 底部提示 - 不设 layoutWeight → 由内容撑开(固定高度)
Text(‘提示’).fontSize(11);
}
.height(‘100%’)
空间分配计算:
总高度 = 父容器 height(100%)
= 标题区高度 + 列表区高度 + 底部提示高度
= 内容撑开高度 + layoutWeight(1)·剩余空间 + 内容撑开高度
布局效果:标题和底部按内容高度固定,列表区占满中间所有空间。不论屏幕大小如何变化,这种「头尾固定、中间自适应」的布局模式都稳定可靠。
8.3 Column + Row 的组合模式
Column 和 Row 在 ArkUI 中的关系类似于 Flexbox 中的 flex-direction: column 和 flex-direction: row。项目中最常见的组合模式:
模式 代码结构 效果
列表条目 Row[图标 + Column(标题+副标题) + 箭头] 左中右经典模式
搜索栏 Row[TextInput(layoutWeight=1) + Button] 输入框+按钮组合
按钮组 Row[Button + Button(marginLeft)] 水平排列的操作按钮
计数行 Row[Text] 单行信息展示
9. 编译与调试经验
9.1 List 子组件限制
错误信息:
The component ‘List’ can only have the child component ‘ListItem,Section,ListItemGroup’.
The ‘ListItem’ component can only be nested in the ‘List,ListItemGroup’ parent component.
原因: 最初的结构是:
List() {
NavRouter() { // ← List 无法接受 NavRouter 作为子组件
ListItem() { } // ← ListItem 不在 List 的直接子节点中
}
}
解决方案: 承认 List 的结构约束,使用 @State 条件渲染替代 NavRouter 做页面切换。
List() {
ListItem() { } // ✅ 唯一允许的直接子组件
}
启示: ArkTS 的组件约束是编译时检查的,违反就会报错。不要试图绕过这些约束,而是理解它们背后的设计意图——List 的子组件限制确保了虚拟滚动机制的正确工作。
9.2 NavRouter 废弃的迁移路线
警告信息:
WARN: ‘NavRouter’ has been deprecated.
NavRouter 在 API 12 中被标记废弃,建议的替代方案是:
旧方案 新方案
NavRouter + NavDestination Navigation + 条件渲染(if/else)
路由声明式导航 状态驱动导航
隐式耦合 显式数据流
9.3 NavigationTitleMode 类型约束
Navigation 的 titleMode 属性接受枚举值而非字符串:
// ✅ 正确:
.titleMode(NavigationTitleMode.Mini)
// ❌ 错误:
.titleMode(‘Mini’)
同样,NavigationMode.Stack 也不能写成 ‘Stack’。这是 ArkTS 中常见的枚举使用方式。
- 效果展示与交互流程
10.1 列表首页布局
┌──────────────────────────────────────┐
│ ← ArkTS 布局导航 │ ← Navigation 标题栏(Mini 模式)
├──────────────────────────────────────┤
│ 📖 ArkTS 布局导航 │
│ Column + List + Navigation 协作布局 │ ← 标题区 (Indigo 50)
│ 点击列表项查看详情… │
├──────────────────────────────────────┤
│ ┌─ 🔍 搜索布局知识… ──┬─ ⇅ 排序 ─┐│ ← 搜索操作栏
│ └────────────────────────┴─────────┘│
│ 📚 共 8 个知识点 │
│ ┌──────────────────────────────┐ │
│ │ 📱 鸿蒙原生开发 › │ │ ← ListItem × 8
│ │ ArkTS 语言特性与声明式 UI │ │ (白色卡片, 圆角12)
│ ├──────────────────────────────┤ │
│ │ 📐 Column 布局 › │ │
│ │ 弹性列布局及其应用场景 │ │
│ ├──────────────────────────────┤ │
│ │ 📋 List 虚拟滚动 › │ │
│ │ 高性能长列表渲染机制 │ │
│ ├──────────────────────────────┤ │
│ │ 🧭 Navigation 导航 › │ │
│ │ 页面容器与状态驱动的切换 │ │
│ └──────────────────────────────┘ │
│ 💡 点击任意条目查看详情 → │
└──────────────────────────────────────┘
10.2 详情页布局
┌──────────────────────────────────────┐
│ ← ArkTS 布局导航 │ ← 显示返回按钮
├──────────────────────────────────────┤
│ │
│ 📱 │
│ 鸿蒙原生开发概述 │ ← 详情标题区
│ ID: 1 | 导航层级: 1 | ArkTS 语言… │
│ │
│ ┌─ 📝 详细介绍 ─────────────────┐ │
│ │ ArkTS 是鸿蒙原生应用的首选… │ │ ← 内容卡片
│ │ │ │
│ │ 核心特性: │ │
│ │ • @Entry/@Component… │ │
│ │ • @State 驱动 UI… │ │
│ │ • 链式属性调用… │ │
│ └────────────────────────────────┘ │
│ │
│ [← 返回列表] [📌 收藏] │ ← 操作按钮
│ │
│ ┌─ 💡 Column+List+Navigation… ─┐ │
│ │ • Column:垂直组合… │ │ ← 原理说明
│ │ • List:虚拟滚动… │ │
│ │ • Navigation:管理页面切换… │ │
│ └────────────────────────────────┘ │
└──────────────────────────────────────┘
10.3 典型交互流程
启动应用 → 看到列表首页,Navigation 标题栏显示"ArkTS 布局导航"
滑动列表 → List 虚拟滚动流畅工作,所有 ListItem 有白色卡片背景和圆角
点击"List 虚拟滚动"条目 → currentDetail 赋值为该条目数据 → 切换到详情页
详情页展示 → 顶部大图标 + 标题,中间详细介绍内容,底部有原理说明
点击「← 返回列表」按钮 → currentDetail 置 null → 回到列表页
再次进入另一个条目 → 看到完全不同的详情内容
在搜索框中输入文字 → searchValue 状态更新(虽未实现实际搜索过滤,但数据流已就位) - 性能分析与最佳实践
11.1 列表渲染性能
以 8 条数据为例,List 的渲染性能分析:
指标 值
数据总量 8 条
可见区域项数 约 4 条
渲染项数(含缓存) 约 7 条
内存占用 ≈ 7 × (ListItem + 子组件)
滑动帧率 60fps(流畅)
对于 8 条数据,List 的优势不明显——Scroll + Column 也能流畅渲染。但 List 的优势在于当数据扩展到成百上千条时,性能表现不变。
11.2 最佳实践清单
实践 说明
1 List 子组件只能是 ListItem 不支持 NavRouter、Column 等直接作为 List 的子组件
2 使用 @State 驱动页面切换 比 NavRouter 更简洁、灵活、无废弃风险
3 Column 每层设置 width(‘100%’) 防止内容宽度异常
4 详情页使用 Scroll + Column 比 List 更适合内容形式多样的页面
5 数据模型分离显示字段 title 和 detailTitle 分开,适配不同场景
6 layoutWeight(1) 用于弹性撑满 让列表区自适应剩余空间
7 Navigation 与 @State 联动 hideBackButton 绑定状态表达式
8 ForEach 配合 List 使用 数据驱动列表渲染,View 与 Data 解耦
12. 扩展方向
12.1 搜索过滤功能
基于现有的 searchValue 状态,可以轻松实现实时搜索过滤:
get filteredList(): ListItemData[] {
const keyword = this.searchValue.trim().toLowerCase();
if (keyword === ‘’) return this.listData;
return this.listData.filter(item =>
item.title.toLowerCase().includes(keyword) ||
item.subtitle.toLowerCase().includes(keyword)
);
}
然后在 ForEach 中替换数据源:
ForEach(this.filteredList, (item: ListItemData) => { … });
12.2 分组列表——Section 组件
当列表数据需要分组时,可以使用 ListItemGroup 或 Section 组件:
List() {
Section({ header: ‘基础布局’ }) {
ForEach(basicLayoutItems, (item) => { ListItem() { … } });
}
Section({ header: ‘导航路由’ }) {
ForEach(navigationItems, (item) => { ListItem() { … } });
}
}
12.3 路由传参与多级导航
对于更复杂的导航需求,可以扩展 currentDetail 为导航栈:
@State navStack: ListItemData[] = [];
// 推入
onPush(item: ListItemData): void {
this.navStack.push(item);
}
// 弹出
onPop(): void {
this.navStack.pop();
}
// 当前页面
get currentPage(): ListItemData | null {
return this.navStack.length > 0
? this.navStack[this.navStack.length - 1]
: null;
}
这种手动的导航栈可以实现任意深度的页面跳转。
- 总结
本文通过一个完整的 HarmonyOS NEXT ArkTS 项目,深入解析了 Column + List + Navigation 协作布局 的实现原理和工程实践。
知识点 核心内容
Column 垂直布局容器,嵌套组合标题、搜索栏、列表、底部提示
List 虚拟滚动列表,space(8) 间距,cachedCount(3) 缓存
Navigation 导航容器,titleMode(Mini) 紧凑标题,@State 联动返回按钮
@State 驱动 currentDetail 控制列表/详情切换,单一数据源
ForEach 数据驱动渲染,8 条知识条目循环生成
Row + Column 「左图标 + 中标题 + 右箭头」经典列表项布局模式
Scroll + Column 详情页的富内容组合,适合形式多样的页面展示
Column、List、Navigation 三者协作的核心理念可以概括为一句话:
Column 搭骨架,List 管内容,Navigation 控流程。
在实际的鸿蒙原生应用开发中,这一协作模式将出现在绝大多数页面中。无论是新闻列表、商品目录、好友通讯录还是设置页面,都可以复用本文介绍的架构思路。
希望本文能帮助你在鸿蒙原生应用开发的道路上更进一步。如果你对布局和导航有更多的创意,欢迎在实际项目中大胆探索。
参考资料:
HarmonyOS NEXT 开发者文档 — ArkUI 组件参考(Column、List、Navigation)
ArkTS 装饰器参考 — @State / @Prop / @Link / @Watch
Material Design 3 — 列表与导航模式
声明式 UI 编程范式 — 状态驱动视图更新
虚拟滚动算法原理 — 计算机图形学中的视锥体裁剪
更多推荐



所有评论(0)