一、List

1.概述

列表是一种非常有用且功能强大的容器,它常用于呈现同类型或多类型数据集合,例如图片、文本、音乐、通讯录、购物清单等。列表对于显示大量内容而不耗费过多空间和内存是非常有帮助的,因为当列表项数量超过屏幕大小时,可以自动提供滚动功能。这使得列表成为构建结构化、可滚动信息的理想容器。

使用列表可以轻松、高效地显示信息。使用List组件,可以按垂直或水平方向线性排列子组件,这些子组件可以是单个视图,也可以使用ForEach迭代一组行或列,或混合任意数量的单个视图和ForEach结构,构建一个灵活的列表。同时,List组件支持使用条件渲染、循环渲染、懒加载等渲染控制方式生成子组件,使得列表变得更加灵活和高效。

列表是一种非常实用的容器,适用于呈现各种类型的数据集合,并且可以为用户提供高效、流畅的滚动浏览体验。

2.布局与约束

ListItemGroup是一个用于列表数据分组展示的组件,它的子组件也是ListItem。

  • ListItem是单个列表项的表示,每个ListItem可以包含一个单独的子组件,用于更详细的展示该列表项的内容。

  • ListItemGroup可以通过增加、删除子组件或调整子组件的位置,来展示不同的分组数据。同时,你也可以扩展ListItem和ListItemGroup,添加更多的属性和方法,以适配不同的使用场景。

2.1 布局

1、垂直滚动列表

2、水平滚动列表(左:单行;右:多行)

2.2 约束

1、列表的主轴与交叉轴

如果List组件主轴或交叉轴方向设置了尺寸,则其对应方向上的尺寸为设置值。

2、一个垂直列表B没有设置高度时,其父组件A高度为200vp,若其所有子组件C的高度总和为150vp,则此时列表B的高度为150vp。

3、同样是没有设置高度的垂直列表B,其父组件A高度为200vp,若其所有子组件C的高度总和为300vp,则此时列表B的高度为200vp。

3.开发布局

3.1 设置主轴方向

List() {
...
}
.listDirection(Axis.Horizontal)

List组件默认主轴方向是垂直的,可以自动构建垂直滚动列表,无需手动设置。如果需要构建水平滚动列表,只需要将List组件的listDirection属性设置为Axis.Horizontal即可。需要注意的是,listDirection属性的默认值为Axis.Vertical,即默认情况下List组件的主轴方向是垂直方向。

更多鸿蒙最新技术知识点,请关注作者博客:https://t.doruo.cn/14DjR1rEY

3.2 设置交叉轴布局

List组件的交叉轴布局可以通过lanes和alignListItem属性进行设置。lanes属性用于确定交叉轴排列的列表项数量,alignListItem用于设置子组件在交叉轴方向的对齐方式。一般情况下,List组件的lanes属性被用于在不同尺寸的设备上自适应构建不同行数或列数的列表。lanes属性的取值类型为"number | LengthConstrain",即整数或者LengthConstrain类型。

List() {
...
}
.lanes(2)
List() {
...
}
.lanes({ minLength: 200, maxLength: 300 })
List() {
...
}
.alignListItem(ListItemAlign.Center)
  • lanes:设置列数
  • alignListItem:设置对齐方式

4.案例

4.1 在列表中显示数据

@Entry
@Component
struct Index {
build() {
List() {
ListItem() {
Text('北京').fontSize(24)
} ListItem() {
Text('杭州').fontSize(24)
} ListItem() {
Text('上海').fontSize(24)
}
}
.backgroundColor('#FFF1F3F5')
.alignListItem(ListItemAlign.Center)
}
}


@Entry
@Component
struct Index {
build() {
List() {
ListItem() {
Row() {
Image($r('app.media.app_icon'))
.width(40)
.height(40)
.margin(10) Text('小明')
.fontSize(20)
}
} ListItem() {
Row() {
Image($r('app.media.app_icon'))
.width(40)
.height(40)
.margin(10) Text('小红')
.fontSize(20)
}
}
}
}
}

4.1 迭代列表内容

import util from '@ohos.util';

class Contact {
key: string = util.generateRandomUUID(true);
name: string;
icon: Resource; constructor(name: string, icon: Resource) {
this.name = name;
this.icon = icon;
}
}
@Entry
@Component
struct Index {
private contacts = [
new Contact('小明', $r("app.media.app_icon")),
new Contact('小红', $r("app.media.app_icon")),
new Contact('张三', $r("app.media.app_icon")),
new Contact('李四', $r("app.media.app_icon")),
] build() {
List() {
ForEach(this.contacts, (item: Contact) => {
ListItem() {
Row() {
Image(item.icon)
.width(40)
.height(40)
.margin(10)
Text(item.name).fontSize(20)
}
.width('100%')
.justifyContent(FlexAlign.Start)
}
}, item => item.key+item.name)
}
.width('100%')
}
}

4.3 自定义列表样式

️4.3.1 内容间距
List({ space: 10 }) {
...
}

️4.3.2 添加分隔线
List() {
...
}
.divider({
strokeWidth: 1,
startMargin: 60,
endMargin: 10,
color: '#ffe9f0f0'
})

️4.3.3 添加滚动条
List() {
...
}
.scrollBar(BarState.Auto)

️4.3.4 分组列表

1、直接手动分钟

@Component
struct ContactsList {
... @Builder itemHead(text: string) {
// 列表分组的头部组件,对应联系人分组A、B等位置的组件
Text(text)
.fontSize(20)
.backgroundColor('#fff1f3f5')
.width('100%')
.padding(5)
} build() {
List() {
ListItemGroup({ header: this.itemHead('A') }) {
// 循环渲染分组A的ListItem
...
}
... ListItemGroup({ header: this.itemHead('B') }) {
// 循环渲染分组B的ListItem
...
}
...
}
}
}

2、遍历分组

contactsGroups: object[] = [
{
title: 'A',
contacts: [
new Contact('艾佳', $r('app.media.iconA')),
new Contact('安安', $r('app.media.iconB')),
new Contact('Angela', $r('app.media.iconC')),
],
},
{
title: 'B',
contacts: [
new Contact('白叶', $r('app.media.iconD')),
new Contact('伯明', $r('app.media.iconE')),
],
},
...
]
List() {
// 循环渲染ListItemGroup,contactsGroups为多个分组联系人contacts和标题title的数据集合
ForEach(this.contactsGroups, item => {
ListItemGroup({ header: this.itemHead(item.title) }) {
// 循环渲染ListItem
ForEach(item.contacts, contact => {
ListItem() {
...
}
}, item => item.key)
}
...
})
}

️4.3.5 添加粘性标题(官方)
@Component
struct ContactsList {
// 定义分组联系人数据集合contactsGroups数组
... @Builder itemHead(text: string) {
// 列表分组的头部组件,对应联系人分组A、B等位置的组件
Text(text)
.fontSize(20)
.backgroundColor('#fff1f3f5')
.width('100%')
.padding(5)
} build() {
List() {
// 循环渲染ListItemGroup,contactsGroups为多个分组联系人contacts和标题title的数据集合
ForEach(this.contactsGroups, item => {
ListItemGroup({ header: this.itemHead(item.title) }) {
// 循环渲染ListItem
ForEach(item.contacts, contact => {
ListItem() {
...
}
}, item => item.key)
}
...
})
}
.sticky(StickyStyle.Header) // 设置吸顶,实现粘性标题效果
}
}

image

️4.3.6 控制滚动位置(官方)
private listScroller: Scroller = new Scroller();
Stack({ alignContent: Alignment.BottomEnd }) {
// 将listScroller用于初始化List组件的scroller参数,完成listScroller与列表的绑定。
List({ space: 20, scroller: this.listScroller }) {
...
}
... Button() {
...
}
.onClick(() => {
// 点击按钮时,指定跳转位置,返回列表顶部
this.listScroller.scrollToIndex(0)
})
...
}

️4.3.7 响应滚动位置(官方)
...
const alphabets = ['#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; @Entry
@Component
struct ContactsList {
@State selectedIndex: number = 0;
private listScroller: Scroller = new Scroller();
... build() {
Stack({ alignContent: Alignment.End }) {
List({ scroller: this.listScroller }) {
...
}
.onScrollIndex((firstIndex: number) => {
this.selectedIndex = firstIndex
// 根据列表滚动到的索引值,重新计算对应联系人索引栏的位置this.selectedIndex
...
})
...
// 字母表索引组件
AlphabetIndexer({ arrayValue: alphabets, selected: 0 })
.selected(this.selectedIndex)
...
}
}
}

️4.3.8 响应列表项侧滑(官方)
@Entry
@Component
struct MessageList {
@State messages: object[] = [
// 初始化消息列表数据
...
]; @Builder itemEnd(index: number) {
// 侧滑后尾端出现的组件
Button({ type: ButtonType.Circle }) {
Image($r('app.media.ic_public_delete_filled'))
.width(20)
.height(20)
}
.onClick(() => {
this.messages.splice(index, 1);
})
...
}
build() {
...
List() {
ForEach(this.messages, (item, index) => {
ListItem() {
...
}
.swipeAction({ end: this.itemEnd.bind(this, index) }) // 设置侧滑属性
}, item => item.id.toString())
}
...
}
}

️4.3.9 给列表项添加标记(官方)
Badge({
count: 1,
position: BadgePosition.RightTop,
style: { badgeSize: 16, badgeColor: '#FA2A2D' }
}) {
// Image组件实现消息联系人头像
...
}
...

️4.3.10 下拉刷新与上拉加载(官方)

案例:https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_NewsDataLoad?ha_linker=eyJ0cyI6MTcwMTY1Njg2NjU1MywiaWQiOiJjNDFjZDY5NTVjZDQ0NTBjZWE1ZWQxOTQ0MzZkODJkNSJ9

第三方组件:https://gitee.com/openharmony-sig/PullToRefresh

更多鸿蒙最新技术知识点,请关注作者博客:https://t.doruo.cn/14DjR1rEY

️4.3.11 编辑列表(官方)

1、定义列表项数据结构

import util from '@ohos.util';

export class ToDo {
key: string = util.generateRandomUUID(true);
name: string; constructor(name: string) {
this.name = name;
}
}

2、初始化数据

@State toDoData: ToDo[] = [];
private availableThings: string[] = ['读书', '运动', '旅游', '听音乐', '看电影', '唱歌'];

3、构建列表布局和列表项

List({ space: 10 }) {
ForEach(this.toDoData, (toDoItem) => {
ListItem() {
...
}
}, toDoItem => toDoItem.key)
}

4、响应用户确定新增事件,更新列表数据

Text('+')
.onClick(() => {
TextPickerDialog.show({
range: this.availableThings,
onAccept: (value: TextPickerResult) => {
this.toDoData.push(new ToDo(this.availableThings[value.index])); // 新增列表项数据toDoData
},
})
})

️4.3.12 删除列表项(官方)

1、以待办列表为例,通过监听列表项的长按事件,当用户长按列表项时,进入编辑模式。

// ToDoListItem.ets

Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
...
}
.gesture(
GestureGroup(GestureMode.Exclusive,
LongPressGesture()
.onAction(() => {
if (!this.isEditMode) {
this.isEditMode = true; //进入编辑模式
this.selectedItems.push(this.toDoItem); // 记录长按时选中的列表项
}
})
)
)

2、在待办列表中,通过勾选框的勾选或取消勾选,响应用户勾选列表项变化,记录所有选择的列表项。

// ToDoListItem.ets

if (this.isEditMode) {
Checkbox()
.onChange((isSelected) => {
if (isSelected) {
this.selectedItems.push(this.toDoItem) // 勾选时,记录选中的列表项
} else {
let index = this.selectedItems.indexOf(this.toDoItem)
if (index !== -1) {
this.selectedItems.splice(index, 1) // 取消勾选时,则将此项从selectedItems中删除
}
}
})
...
}

3、需要响应用户点击删除按钮事件,删除列表中对应的选项

// ToDoList.ets

Button('删除')
.onClick(() => {
// 删除选中的列表项对应的toDoData数据
let leftData = this.toDoData.filter((item) => {
return this.selectedItems.find((selectedItem) => selectedItem !== item);
}) this.toDoData = leftData;
this.isEditMode = false;
})
...

4.4 长列表的处理

循环渲染适用于短列表。但当构建具有大量列表项的长列表时,直接采用循环渲染方式会一次性加载所有的列表元素,导致页面启动时间过长,影响用户体验。因此,推荐使用数据懒加载(LazyForEach)方式实现按需迭代加载数据,从而提升列表性能。具体实现可参考数据懒加载章节中的示例。

当使用懒加载方式渲染列表时,为了更好的列表滚动体验,并减少列表滑动时出现白块,List组件提供了cachedCount参数。该参数用于设置列表项缓存数量,只在懒加载LazyForEach中生效。

List() {
LazyForEach(this.dataSource, item => {
ListItem() {
...
}
})
}.cachedCount(3)

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing,不定期分享原创知识。
  • 更多鸿蒙最新技术知识点,请关注作者博客:https://t.doruo.cn/14DjR1rEY

鸿蒙HarmonyOS实战-ArkUI组件(List)的更多相关文章

  1. 鸿蒙的远程交互组件应用及微信小程序的远程交互组件应用

    注:鸿蒙的远程交互组件应用相对复杂 ,访问网络时,首先要配置网络权限,华为官方文档有问题,在此引用我老师配置的模板,见附件 过程:1.导入鸿蒙的网络请求模块fetch 2.发起对服务器的请求(在这过程 ...

  2. 微信小程序入门与实战 常用组件API开发技巧项目实战*全

    第1章 什么是微信小程序? 第2章 小程序环境搭建与开发工具介绍 第3章 从一个简单的“欢迎“页面开始小程序之旅 第4章 第二个页面:新闻阅读列表 第5章 小程序的模板化与模块化 第6章 构建新闻详情 ...

  3. 手把手带你体验鸿蒙 harmonyOS

    wNlRGd.png 前言 本文已经收录到我的 Github 个人博客,欢迎大佬们光临寒舍: 我的 GIthub 博客 学习导图 image.png 一.为什么要尝鲜 harmonyos? wNlfx ...

  4. 最全华为鸿蒙 HarmonyOS 开发资料汇总

    开发 本示例基于 OpenHarmony 下的 JavaScript UI 框架,进行项目目录解读,JS FA.常用和自定义组件.用户交互.JS 动画的实现,通过本示例可以基本了解和学习到 JavaS ...

  5. 鸿蒙HarmonyOS应用开发落地实践,Harmony Go 技术沙龙落地北京

    12月26日,华为消费者BG软件部开源中心与51CTO Harmony OS技术社区携手,共同主办了主题为"Harmony OS 应用开发落地实践"的 Harmony Go 技术沙 ...

  6. 鸿蒙开源第三方件组件——轮播组件Banner

    目录: 1.功能展示 2.Sample解析 3.Library解析 4.<鸿蒙开源第三方组件>系列文章合集 前言 基于安卓平台的轮播组件Banner(https://github.com/ ...

  7. 14-Flutter移动电商实战-ADBanner组件的编写

    拨打电话的功能在app里也很常见,比如一般的外卖app都会有这个才做.其实Flutter本身是没给我们提供拨打电话的能力的,那我们如何来拨打电话那? 1.编写店长电话模块 这个小伙伴们一定轻车熟路了, ...

  8. 13-Flutter移动电商实战-ADBanner组件的编写

    1.AdBanner组件的编写 我们还是把这部分单独出来,需要说明的是,这个Class你也是可以完全独立成一个dart文件的.代码如下: 广告图片class AdBanner extends Stat ...

  9. iview实战 : 树形组件自定义

    Tree树形组件是 iview 中相对复杂的一个组件. 自定义节点内容 使用强大的 Render 函数可以自定义节点显示内容和交互,比如添加图标,按钮等. ——官方文档 但官方的 example 只有 ...

  10. ArkUI 组件 Props

    在上一篇博客文章中简单地提到了 Props . 在使用 Props 时需要注意到一个点,子组件从寄主页面传递过来的值是单向的,也就是子组件不能直接修改传递下来的值,即单向性. 以上篇文章定义的头像组件 ...

随机推荐

  1. vue+element-ui项目搭建实战

    1.使用vue ui创建vue工程 利用vue-cli提供的图形化工具快速搭建vue工程: 命令行运行:vue ui 工程结构说明 build:项目构建webpack(打包器)相关代码 config: ...

  2. C++ 多线程的错误和如何避免(7)

    要以相同顺序获取多个锁 多线程在加锁解锁时,可能会出现死锁问题,比如, 线程 1 在加锁 mutex A 后,继续尝试获取 mutex B,而 mutex B 已经被线程 2 获取,而线程 2 在等待 ...

  3. 使用GDI时如何确定是否有内存泄漏

    在创建GDI对象时,比如创建笔,画刷等对象时,在调用完之后忘记删除对象了,会造成内存泄漏 我们可以通过任务管理器来快速的查看 启动任务管理器(右键单击Windows任务栏以选择任务管理器) 在Wind ...

  4. go语言编程常见问题

    在Goland中运行单元测试报错Error: Cannot find package 如下图,在Goland中运行单元测试时报错:"Error: Cannot find package&qu ...

  5. Avalonia 跨平台框架中如何以正确的姿势使用 OpenGL ES 渲染。

    前言 这是一篇很水的文章,没有任何技术含量,在 Github 已经有很多人对 Avalonia 的 OpenGL ES 支持进行了讨论,但是我还是想写一篇文章来记录一下我是如何在 Avalonia 中 ...

  6. 主流开源分布式图计算框架 Benchmark

    本文由美团 NLP 团队高辰.赵登昌撰写,首发于 Nebula Graph Community 公众号 前言 随着近年来数据的爆炸式增长,如何高效地分析处理数据,在业界一直备受关注.现实世界中的数据往 ...

  7. .Net之配置文件自定义

    前文讲获取配置文件内容的时候,是获取默认的appsettings.json配置文件的配置,下面说明下如何进行自定义配置文件获取 1. Json Provider 1.1 构建独立的IConfigura ...

  8. Volatile关键字原理

    转载请标明:https://www.cnblogs.com/tangZH/p/15113505.html 一.如果一个变量被volatile关键字修饰,那么所有线程都是可见的.所谓可见就是,当一条线程 ...

  9. node.js在win7下安装,并测试是否安装成功

    1.node.js去官网下载,下载完,像平时安装软件一样 2.把下面的测试文件,放到安装目录下,本文是放到:D:\Program Files\nodejs下 var http = require(&q ...

  10. 1、eureka的注册流程

    客户端注册到服务端是通过http请求的 涉及到多级缓存 register注册表 源码精髓:多级缓存设计思想 在拉取注册表的时候: 首先从ReadOnlyCacheMap里查缓存的注册表. 若没有,就找 ...