一、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. C++ 多线程的错误和如何避免(8)

    不要重复获取同一个锁 问题:在获得一个锁并且没有释放该锁的前提下,再次尝试获取该锁会报错. 比如, #include <iostream> #include <thread> ...

  2. win32 - Screen to bmp file(BYTE)

    void WINAPI CaptureScreenIntoFile() { BITMAPFILEHEADER bfHeader; BITMAPINFOHEADER biHeader; HGDIOBJ ...

  3. win32-UI Automation

    使用UI Automation遍历窗口的所有控件标题和类 #include <Windows.h> #include <stdio.h> #include <UIAuto ...

  4. 一个有意思的问题:Kafka的消费Offset会溢出吗

    最近在项目上接入公司APP产品的用户点击日志数据时,发现消费者组的Offset值非常大,才一天的时间,已提交的Offset值就有千亿级别了. 于是不禁想了一个问题:假设一个Topic就只有一个Part ...

  5. linux下安装nginx(编译安装)及反向代理及负载均衡

    首先卸载掉之前用yum命令下载的nginx yum remove nginx 安装nginx需要的依赖库 yum install -y gcc patch libffi-devel python-de ...

  6. 现代 CSS 解决方案:accent-color 强调色

    accent-color 是从 Chrome 93 开始被得到支持的一个不算太新属性.之前一直没有好好介绍一下这个属性.直到最近在给一些系统整体切换主题色的时候,更深入的了解了一下这个属性. 简单而言 ...

  7. Centos8上安装Redis5.X

    一.下载Redis 下载地址:wget http://download.redis.io/releases/redis-5.0.7.tar.gz 解压:tar -xzvf redis-5.0.7.ta ...

  8. 第一篇博客——MarkDown语法

    Markdown学习 标题 三级标提 四级标题 字体 Hello World ! 两个星号加粗 Hello World ! 一个星号斜体 Hello World ! Hello World ! 两个波 ...

  9. Javascript之Object、Array

    Object.keys 对象的键转化为数组 Object.values 对象的属性值转化为数组 Object.assign 对象的合并   Array.from() 伪数组对象的属性值转化为数组.类似 ...

  10. 【Azure 云服务】Azure Cloud Service如何来设置固定IP地址(ReservedIP)

    问题描述 在云中环境,部署的应用到云服务(Cloud Service)都是动态的IP地址,所以在添加DNS记录的时候,都是使用CNAME,但如果需要在DNS中添加A记录,则需要一个固定IP. 解决方案 ...