【共创季稿事节】鸿蒙原生 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 路由传参与多级导航

总结

  1. 前言
    在移动应用开发中,「列表页 → 详情页」 是最经典、最高频的页面模式之一。无论是新闻客户端、电商应用、社交平台还是企业工具,几乎每一个应用都包含「条目列表 + 点击查看详情」的交互流程。

在 HarmonyOS NEXT 的 ArkUI 框架中,这一模式可以通过 Column + List + Navigation 三个核心组件的协作来优雅实现:

Column 负责垂直布局的组织------标题、搜索栏、列表内容、底部状态,全部由 Column 组装

List 负责高性能列表渲染------虚拟滚动机制确保即使是上万条数据也不卡顿

Navigation 负责页面导航管理------标题栏、返回按钮、页面切换动画一站式解决

本文以一份完整的 ArkTS 代码项目为载体,从 数据组织、状态管理、布局编排、导航控制 四个维度深入解析这一协作模式。项目本身也是一个「自指」的知识点集合------8 个列表条目本身就是 8 篇 ArkUI 核心知识点的介绍,点击后可查看详细说明。

无论你正在开发第一个鸿蒙应用,还是在寻找更优雅的页面组织方式,本文都将提供从理论到实践的全面参考。

  1. 三大核心组件概述
    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)

  1. 列表---导航协作模式的设计思想

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 的值即可了解当前页面状态

  1. 项目搭建与配置

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 虚拟滚动列表"),两者的信息密度需求不同。

  1. 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 页面栈,返回按钮的显示/隐藏联动本身已经提供了足够的交互提示。

  1. 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() → 详情页

新方案的优势:

更简单:不需要学习和配置路由组件

更灵活:页面内容完全由代码控制

更高效:避免了不必要的组件层级

  1. 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 更灵活。

  1. 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(标题+副标题) + 箭头 左中右经典模式

搜索栏 RowTextInput(layoutWeight=1) + Button 输入框+按钮组合

按钮组 RowButton + Button(marginLeft) 水平排列的操作按钮

计数行 RowText 单行信息展示

  1. 编译与调试经验

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 中常见的枚举使用方式。

  1. 效果展示与交互流程
    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 状态更新(虽未实现实际搜索过滤,但数据流已就位)
  2. 性能分析与最佳实践
    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 解耦

  1. 扩展方向

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.navStackthis.navStack.length - 1

: null;

}

这种手动的导航栈可以实现任意深度的页面跳转。

  1. 总结
    本文通过一个完整的 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 编程范式 --- 状态驱动视图更新

虚拟滚动算法原理 --- 计算机图形学中的视锥体裁剪

相关推荐
HarmonyOS_SDK4 小时前
HarmonyOS SDK 26发布,持续提升鸿蒙应用开发体验与效率
harmonyos
HERR_QQ6 小时前
强化学习的数学原理 学习笔记
人工智能·笔记·学习·自动驾驶
最爱老式锅包肉6 小时前
《HarmonyOS技术精讲-Core Speech Kit(基础语音服务)》第3篇:语音合成——文本到自然语音的魔法
华为·harmonyos
●VON6 小时前
HarmonyKit | 鸿蒙新特性驱动:组件-工具-页面三层架构设计
华为·harmonyos·鸿蒙
MartinYeung57 小时前
[论文学习]DP-Fusion:面向大语言模型的令牌级差分隐私推理-深度解析
人工智能·学习·语言模型
zwenqiyu7 小时前
非线性字符串数据结构串讲
数据结构·c++·学习·算法
●VON8 小时前
HarmonyKit | 鸿蒙新特性应用:10 个开发工具的选型与分类
华为·harmonyos·鸿蒙
MartinYeung58 小时前
[论文学习]SecureGate:通过令牌级门控学习何时安全地揭示PII-深度解析
学习·安全
大鱼>9 小时前
超参数调优进阶:Optuna/Bayesian/Early Stopping
人工智能·学习·机器学习·聚类
liuyicenysabel10 小时前
多服务上线日记二:
服务器·笔记·学习