目录

一、鸿蒙世界的 List 魔法初窥

二、List 组件大揭秘

(一)List 组件基础剖析

(二)ListItem 组件深度探索

(三)ListItemGroup 组件的独特作用

三、使用 ForEach,告别代码冗余

四、个性化定制列表外观

(一)添加分割线,让列表更清晰

(二)调整排列方向,满足多样需求

五、实战演练:打造专属列表

(一)项目准备

(二)数据准备

(三)界面搭建

(四)交互实现

六、总结与展望


一、鸿蒙世界的 List 魔法初窥

当你打开手机,随意进入某个应用,无论是系统自带的设置页面,还是电商平台琳琅满目的商品列表,又或是社交软件里的聊天记录,你都会发现一种极为常见的界面元素 —— 列表。列表就像一位勤劳的 “收纳师”,将各种信息有条不紊地排列展示,让我们能轻松获取想要的内容。在 HarmonyOS 的应用开发宇宙中,List 组件便是构建这些列表页面的神奇 “魔法棒” 。

List 组件在 HarmonyOS 应用开发里占据着举足轻重的地位,广泛应用于各类 APP。从日常工具类应用,到娱乐影音、办公学习类应用,只要涉及信息的有序展示,List 组件几乎无处不在。它的存在,让应用的界面更加整洁、高效,用户交互体验也得到了极大提升。

二、List 组件大揭秘

要想真正掌握 List 组件的魔法,我们得深入到它的内部,了解它的组成和工作原理。List 组件家族主要包括 List、ListItem 和 ListItemGroup 这几个关键成员 ,它们各司其职,共同协作,为我们呈现出丰富多彩的列表界面。

(一)List 组件基础剖析

List 组件就像是一个强大的 “列表管理器”,它按照水平或者竖直方向线性排列子组件,构建出我们常见的列表结构。当列表项较多,内容超过屏幕大小时,它还能自动提供滚动功能,就像给列表加上了一个 “自动翻页器”,让用户可以轻松浏览所有内容 。

在使用 List 组件时,我们可以通过一些参数来定制它的行为。比如 space 参数,它用于设置子组件主轴方向的间隔,就像是给列表项之间添加了一些 “呼吸空间”,让列表看起来更加清晰、舒适。假如我们在构建一个商品列表时,将 space 设置为 10vp,那么每个商品项之间就会有 10vp 的间隔,避免了内容的拥挤。代码示例如下:


List({space: 10}) {

ForEach(this.productList, (product) => {

ListItem() {

// 商品项的具体内容

Text(product.name).fontSize(16)

Text(product.price).fontSize(14).textColor(Color.Gray)

}

})

}

initialIndex 参数则用于设置 List 第一次加载数据时所要显示的第一个子组件的下标。例如,在一个新闻列表应用中,如果我们希望用户打开应用时直接看到第 5 条新闻,就可以将 initialIndex 设置为 4(因为下标从 0 开始)。代码如下:


List({initialIndex: 4}) {

ForEach(this.newsList, (news) => {

ListItem() {

Text(news.title).fontSize(18)

Text(news.publishTime).fontSize(14).textColor(Color.Gray)

}

})

}

scroller 参数是可滚动组件的控制器,用于与可滚动组件进行绑定,就像是给列表的滚动行为安上了一个 “遥控器”,我们可以通过它来控制列表的滚动速度、位置等。不过要注意,它不允许和其他滚动类组件绑定同一个滚动控制对象。

(二)ListItem 组件深度探索

ListItem 组件是 List 组件的 “得力助手”,专门用于展示列表中的具体项。它就像是列表中的一个个 “小单元”,每个 ListItem 都可以包含丰富的内容,如文本、图片、按钮等,为用户呈现出具体的信息。

ListItem 组件也有一些实用的参数。selectable 参数用于设置当前 ListItem 元素是否可以被鼠标框选,在一些需要用户批量操作的场景中非常有用。比如在文件管理应用中,用户可以通过框选多个 ListItem 来进行文件的复制、删除等操作 。代码示例如下:


ListItem({selectable: true}) {

Text('文件1').fontSize(16)

}

swipeAction 参数则用于设置 ListItem 的划出组件,为列表项增添了更多的交互性。通过设置 start 和 end 属性,我们可以定义 ListItem 在划动时左右(垂直布局)或上下(水平布局)两侧显示的组件。例如,在一个任务管理应用中,我们可以设置 ListItem 向左划动时显示 “完成任务” 按钮,向右划动时显示 “删除任务” 按钮,让用户可以更便捷地操作任务。代码如下:


ListItem({

swipeAction: {

start: () => {

Button('完成任务').onClick(() => {

// 完成任务的逻辑

})

},

end: () => {

Button('删除任务').onClick(() => {

// 删除任务的逻辑

})

}

}

}) {

Text('任务1').fontSize(16)

}

(三)ListItemGroup 组件的独特作用

ListItemGroup 组件就像是一个 “分组大师”,用于将列表项进行分组展示,让列表的结构更加清晰、有条理。它的宽度默认充满 List 组件,必须配合 List 组件来使用 。

我们可以通过一些参数来定制 ListItemGroup 的展示效果。header 参数用于设置 ListItemGroup 的头部组件,就像是给分组加上了一个 “标题牌”,让用户一眼就能知道该组的主题。footer 参数则用于设置 ListItemGroup 的尾部组件,比如可以在尾部添加一些说明信息或操作按钮。space 参数用于设置列表项间距,但它只作用于 ListItem 与 ListItem 之间,不作用于 header 与 ListItem、footer 与 ListItem 之间。

当 ListItemGroup 的父组件 List 的 listDirection 属性为 Axis.Vertical(垂直方向)时,不允许设置 ListItemGroup 组件的 height 属性,因为其高度是由 header 高度、footer 高度和所有 ListItem 布局后总高度之和决定的;当父组件 List 的 listDirection 属性为 Axis.Horizontal(水平方向)时,不允许设置 ListItemGroup 组件的 width 属性,其宽度由 header 宽度、footer 宽度和所有 ListItem 布局后总宽度之和决定。而且当前 ListItemGroup 内部的 ListItem 组件不支持编辑、框选、拖拽功能,即 ListItem 组件的 editable、selectable 属性不生效。

例如,在一个音乐播放应用中,我们可以使用 ListItemGroup 将歌曲按照歌手进行分组展示。代码如下:


List() {

ForEach(this.singerList, (singer) => {

ListItemGroup({

header: () => {

Text(singer.name).fontSize(20).fontWeight(FontWeight.Bold)

},

space: 5

}) {

ForEach(singer.songs, (song) => {

ListItem() {

Text(song.title).fontSize(16)

}

})

}

})

}

在这个例子中,每个歌手作为一个 ListItemGroup,其 header 显示歌手名字,内部的 ListItem 则是该歌手的歌曲,通过这种方式,用户可以更方便地浏览和选择自己喜欢的歌曲。

三、使用 ForEach,告别代码冗余

在构建列表时,如果我们手动一个个地编写 ListItem 组件,当列表项数量较多时,代码会变得冗长且难以维护 。这时候,ForEach 就像是一位 “代码优化大师”,通过循环渲染的方式,让我们可以简洁高效地构建列表。

ForEach 的使用方法并不复杂。它有三个主要参数:arr、itemGenerator 和 keyGenerator 。arr 是需要迭代的数组,也就是我们的数据源,就好比是装满各种食材的 “原料库”。itemGenerator 是子组件生成函数,它会为数组中的每个元素创建对应的组件,就像是一位 “厨师”,根据不同的食材制作出一道道美味的 “菜肴”(组件)。keyGenerator 是数组项唯一键值生成函数,虽然是可选参数,但为了让开发框架能够更好地识别数组的更改,提高性能,建议提供该参数,它就像是给每道 “菜肴” 贴上一个独一无二的 “标签”,方便框架管理和识别。

下面我们通过一个具体的代码示例来深入理解 ForEach 的使用。假设我们要构建一个水果列表,每个列表项显示水果的名称和价格。首先,我们定义一个水果数组:


private fruits: Array<{name: string, price: number}> = [

{name: '苹果', price: 5},

{name: '香蕉', price: 3},

{name: '橙子', price: 4}

]

然后,在 List 组件中使用 ForEach 来循环渲染这些水果:


List() {

ForEach(this.fruits, (fruit) => {

ListItem() {

Column() {

Text(fruit.name).fontSize(16).fontWeight(FontWeight.Bold)

Text(`价格: ${fruit.price}元/斤`).fontSize(14).textColor(Color.Gray)

}

}

}, fruit => fruit.name)

}

在这个例子中,this.fruits 就是 arr 参数,它提供了我们要展示的水果数据。(fruit) => {...} 是 itemGenerator 函数,它为每个水果创建一个 ListItem 组件,并在其中展示水果的名称和价格。fruit => fruit.name 是 keyGenerator 函数,它使用水果的名称作为唯一键值,这样框架就能准确地识别每个水果项,当数据发生变化时,能高效地更新对应的组件。

当数据源发生变化时,ForEach 也能很好地响应。比如,我们添加一个新的水果到数组中:


addFruit() {

this.fruits.push({name: '草莓', price: 8})

}

调用这个函数后,ForEach 会自动检测到数组的变化,并重新渲染列表,将新的水果项添加到列表中,无需我们手动去处理复杂的 DOM 更新操作。

四、个性化定制列表外观

掌握了 List 组件的基本使用后,我们还可以通过一些属性和方法对列表的外观进行个性化定制,让列表更加符合应用的风格和需求 。

(一)添加分割线,让列表更清晰

在一些场景中,为了让列表项之间的区分更加明显,我们可以给列表添加分割线 。List 组件提供了 divider 属性来实现这一功能。divider 属性包含多个参数,让我们可以精细地控制分割线的样式。

strokeWidth 参数用于设置分割线的线宽,就像是给分割线调整 “粗细”。比如,我们将 strokeWidth 设置为 1vp,分割线就会比较细;如果设置为 3vp,分割线就会更粗一些 。代码示例如下:


List() {

ForEach(this.fruits, (fruit) => {

ListItem() {

Text(fruit.name).fontSize(16)

}

})

}.divider({strokeWidth: 1})

color 参数用于设置分割线的颜色,为分割线赋予不同的色彩,让列表更具视觉层次感。我们可以将 color 设置为 Color.Gray,这样分割线就会显示为灰色 。代码如下:


List() {

ForEach(this.fruits, (fruit) => {

ListItem() {

Text(fruit.name).fontSize(16)

}

})

}.divider({strokeWidth: 1, color: Color.Gray})

startMargin 和 endMargin 参数分别用于设置分割线距离列表侧边起始端和结束端的距离,就像是给分割线在列表两侧添加一些 “留白”。不过要注意,endMargin + startMargin 不能超过列宽度,而且 startMargin 和 endMargin 不支持设置百分比 。例如,我们将 startMargin 设置为 10vp,endMargin 设置为 10vp,代码如下:


List() {

ForEach(this.fruits, (fruit) => {

ListItem() {

Text(fruit.name).fontSize(16)

}

})

}.divider({strokeWidth: 1, color: Color.Gray, startMargin: 10, endMargin: 10})

需要注意的是,List 的分割线画在主轴方向两个子组件之间,第一个子组件上方和最后一个子组件下方不会绘制分割线。在多列模式下,ListItem 与 ListItem 之间的分割线起始边距从每一列的交叉轴方向起始边开始计算,其他情况从 List 交叉轴方向起始边开始计算 。通过合理设置这些参数,我们可以打造出清晰、美观的列表分割线效果。

(二)调整排列方向,满足多样需求

List 组件里面的列表项默认是按垂直方向排列的,这适用于大多数常见的列表场景,比如新闻列表、任务列表等 。但在某些情况下,我们可能需要让列表沿水平方向排列,以满足特殊的布局需求。这时候,我们可以通过设置 List 组件的 listDirection 属性来轻松实现这一效果 。

listDirection 参数的类型是 Axis,它定义了两种类型:Vertical(默认值)和 Horizontal 。当 listDirection 为 Vertical 时,子组件 ListItem 在 List 容器组件中呈纵向排列,这是我们最常见的列表排列方式。例如,在一个音乐播放应用的歌曲列表中,每首歌曲作为一个 ListItem 垂直排列,方便用户上下浏览歌曲 。代码如下:


List() {

ForEach(this.songs, (song) => {

ListItem() {

Text(song.title).fontSize(16)

Text(song.singer).fontSize(14).textColor(Color.Gray)

}

})

}

当我们将 listDirection 设置为 Horizontal 时,子组件 ListItem 在 List 容器组件中呈横向排列 。比如,在一个图片展示应用中,我们可以将图片以水平列表的形式展示,用户可以左右滑动查看更多图片 。代码示例如下:


List({listDirection: Axis.Horizontal}) {

ForEach(this.images, (image) => {

ListItem() {

Image(image.url).width(200).height(200)

}

})

}

通过对比这两种排列方向的效果,我们可以发现,垂直排列更适合展示大量的文本信息,方便用户逐行阅读;而水平排列则更适合展示图片、图标等元素,能够充分利用屏幕的宽度,营造出不同的视觉效果 。开发者可以根据应用的具体需求,灵活选择列表的排列方向,为用户带来更加友好、舒适的交互体验。

五、实战演练:打造专属列表

理论知识掌握得再扎实,也需要通过实战来检验。下面,我们就通过一个具体的项目,来实际运用 List 组件,打造一个专属的列表页面 。

(一)项目准备

首先,我们要创建一个新的 HarmonyOS 项目。打开 DevEco Studio,点击 “Create Project”,在模板选择界面中,选择 “Empty Ability”,然后点击 “Next” 。在项目配置页面,设置好项目名称、包名、保存路径等信息,点击 “Finish”,等待项目创建完成 。

项目创建好后,我们需要配置好开发环境,确保 SDK、Gradle 等依赖都已正确安装和配置。如果在配置过程中遇到问题,可以参考华为官方文档或在开发者社区寻求帮助 。

(二)数据准备

接下来,我们要定义列表所需的数据结构,并创建数据数组,对数据进行初始化 。假设我们要构建一个电影列表,每个列表项需要展示电影的名称、海报、评分等信息,那么我们可以定义如下的数据结构:


export class Movie {

constructor(

public title: string,

public poster: string,

public rating: number

) {}

}

然后,在页面的逻辑代码中,创建数据数组并初始化:


@State movieList: Movie[] = [

new Movie('《肖申克的救赎》', 'https://example.com/shawshank.jpg', 9.7),

new Movie('《霸王别姬》', 'https://example.com/farewell-my-concubine.jpg', 9.6),

new Movie('《阿甘正传》', 'https://example.com/forrest-gump.jpg', 9.5)

]

(三)界面搭建

在项目的pages目录下,找到对应的页面文件(通常是.ets文件),开始搭建界面 。在页面中创建 List 组件,并使用 ForEach 来渲染列表项 。同时,设置列表的样式和属性,如分割线、排列方向等 。


List() {

ForEach(this.movieList, (movie) => {

ListItem() {

Row() {

Image(movie.poster).width(80).height(120).margin(10)

Column() {

Text(movie.title).fontSize(16).fontWeight(FontWeight.Bold)

Text(`评分: ${movie.rating}`).fontSize(14).textColor(Color.Gray)

}.margin(10)

}

}

}, movie => movie.title)

}.divider({strokeWidth: 1, color: Color.Gray})

在这段代码中,我们在 List 组件中使用 ForEach 循环遍历 movieList 数组,为每个电影创建一个 ListItem 。每个 ListItem 包含一个图片和电影的标题、评分信息 。通过设置 divider 属性,为列表添加了分割线,使列表项之间的区分更加明显 。

(四)交互实现

为了提升用户体验,我们还可以为列表项添加点击事件、滑动事件等交互功能 。例如,当用户点击某个电影列表项时,跳转到该电影的详情页面 。


ListItem() {

Row() {

Image(movie.poster).width(80).height(120).margin(10)

Column() {

Text(movie.title).fontSize(16).fontWeight(FontWeight.Bold)

Text(`评分: ${movie.rating}`).fontSize(14).textColor(Color.Gray)

}.margin(10)

}

}.onClick(() => {

// 跳转到电影详情页面的逻辑

router.push({

uri: 'pages/movieDetail?movieId=' + movie.id

})

})

通过为 ListItem 添加 onClick 事件,当用户点击列表项时,会执行相应的跳转逻辑,跳转到电影详情页面,并传递电影的 ID 参数 。

我们还可以为列表添加滑动事件,当用户滑动列表时,记录当前滑动的位置,实现一些特殊的交互效果,比如当用户滑动到列表底部时,自动加载更多数据 。


List() {

// 列表内容

}

.onScroll((event: ScrollEvent) => {

let scrollTop = event.scrollTop

// 当滑动到列表底部时,加载更多数据的逻辑

if (scrollTop + window.innerHeight >= document.body.scrollHeight) {

this.loadMoreMovies()

}

})

在这个例子中,通过监听 List 组件的 onScroll 事件,获取当前的滑动位置 scrollTop 。当滑动位置加上窗口高度大于等于文档总高度时,说明用户滑动到了列表底部,此时调用 loadMoreMovies 函数加载更多电影数据 。

六、总结与展望

通过本文,我们深入探索了 HarmonyOS 中 List 组件构建列表的奥秘 。从 List 组件的基本概念,到其内部成员 ListItem 和 ListItemGroup 的详细解析,再到使用 ForEach 优化代码、个性化定制列表外观,以及通过实战项目将理论知识应用于实际开发,相信大家已经对 List 组件有了较为全面的掌握 。

在实际的 HarmonyOS 应用开发中,List 组件是我们展示数据、提升用户交互体验的得力工具 。它的灵活性和强大功能,为我们创造出各种丰富多样的列表界面提供了可能 。希望大家能够在后续的开发实践中,不断深入探索 List 组件的更多用法和技巧,结合应用的具体需求,充分发挥其优势,打造出更加优秀、流畅、用户体验良好的 HarmonyOS 应用 。

随着 HarmonyOS 生态的不断发展和壮大,相信未来 List 组件也会不断进化和完善,为开发者带来更多便利和惊喜 。让我们一起期待,一起在 HarmonyOS 的开发世界中继续探索前行!

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐