当前位置: 首页 > news >正文

2.7、创建列表(List)

概述

列表是一种复杂的容器,当列表项达到一定数量,内容超过屏幕大小时,可以自动提供滚动功能。它适合用于呈现同类数据类型或数据类型集,例如图片和文本。在列表中显示数据集合是许多应用程序中的常见要求(如通讯录、音乐列表、购物清单等)。

使用列表可以轻松高效地显示结构化、可滚动的信息。通过在List组件中按垂直或者水平方向线性排列子组件ListItemGroup或ListItem,为列表中的行或列提供单个视图,或使用ForEach迭代一组行或列,或混合任意数量的单个视图和ForEach结构,构建一个列表。List组件支持使用条件渲染、循环渲染、懒加载等渲染控制方式生成子组件。

我开发的 Demo 展示

在这里插入图片描述

以下代码均经过我 demo 的实战验证,确保代码和效果对应

布局与约束

列表作为一种容器,会自动按其滚动方向排列子组件,向列表中添加组件或从列表中移除组件会重新排列子组件。

如下图所示,在垂直列表中,List按垂直方向自动排列ListItemGroup或ListItem。

ListItemGroup用于列表数据的分组展示,其子组件也是ListItem。ListItem表示单个列表项,可以包含单个子组件。

布局

List除了提供垂直和水平布局能力、超出屏幕时可以滚动的自适应延伸能力之外,还提供了自适应交叉轴方向上排列个数的布局能力。

利用垂直布局能力可以构建单列或者多列垂直滚动列表,如下图所示。

垂直滚动列表

  • 单列
    在这里插入图片描述
    对应代码
@Entry
@Component
struct ListVerticalPage {@State listItems:Array<String> = []aboutToAppear() {for (var i =0;i< 50;i++) {this.listItems.push("")}}build() {Navigation() {List({space: 5}) {ForEach(this.listItems, ()=> {ListItem() {Stack().width('100%').height(100).backgroundColor('#9dc3e6')}.padding({left:15, right:15})})}}.title('垂直滚动列表').titleMode(NavigationTitleMode.Mini)}
}
  • 多列
    在这里插入图片描述
@Entry
@Component
struct ListMultiVerticalPage {@State listItems:Array<String> = []aboutToAppear() {for (var i =0;i< 50;i++) {this.listItems.push("")}}build() {Navigation() {List({space: 5}) {ForEach(this.listItems, ()=> {ListItem() {Stack().width('100%').height(100).backgroundColor('#9dc3e6')}.padding({left:2,right:2})})}.lanes(2)}.title('垂直滚动多列').titleMode(NavigationTitleMode.Mini)}
}

水平滚动列表

  • 单列
    在这里插入图片描述
    对应代码
@Entry
@Component
struct ListHorizontalPage {@State listItems:Array<String> = []aboutToAppear() {for (var i =0;i< 50;i++) {this.listItems.push(`${i+1}`)}}build() {Navigation() {List({space: 5}) {ForEach(this.listItems, (item:string)=> {ListItem() {Text(item).textAlign(TextAlign.Center).width(50).height(300).backgroundColor('#9dc3e6')}})}.listDirection(Axis.Horizontal)}.title('水平滚动列表').titleMode(NavigationTitleMode.Mini)}
}
  • 多列
    在这里插入图片描述
    对应代码
@Entry
@Component
struct ListMultiHorizontalPage {@State listItems:Array<String> = []aboutToAppear() {for (var i =0;i< 50;i++) {this.listItems.push(`${i+1}`)}}build() {Navigation() {List({space: 5}) {ForEach(this.listItems, (item:string)=> {ListItem() {Text(item).textAlign(TextAlign.Center).width(50).height(300).backgroundColor('#9dc3e6')}})}.lanes(2).listDirection(Axis.Horizontal)}.title('水平滚动多列').titleMode(NavigationTitleMode.Mini)}
}

自定义列表样式

设置内容间距

在初始化列表时,如需在列表项之间添加间距,可以使用 space 参数。例如,在每个列表项之间沿主轴方向添加 55vp 的间距:

在这里插入图片描述

对应局部代码

List({space: 55}) {ForEach(this.listItems, ()=> {ListItem() {Stack().width('100%').height(100).backgroundColor('#9dc3e6')}})
}

添加分隔线

效果图
在这里插入图片描述
对应代码

List() {ForEach(this.listItems, ()=> {ListItem() {Stack().width('100%').height(100).backgroundColor('#9dc3e6')}})
}
.divider({strokeWidth: 1,startMargin: 60,endMargin: 10,color: '#ff0000'
})

添加滚动条

效果图
在这里插入图片描述
对应代码

List() {ForEach(this.listItems, ()=> {ListItem() {Stack().width('100%').height(100).backgroundColor('#9dc3e6')}})
}
.scrollBar(BarState.Auto)
.divider({strokeWidth: 1,color: '#ff0000'
})

支持分组列表

在列表中支持数据的分组展示,可以使列表显示结构清晰,查找方便,从而提高使用效率。分组列表在实际应用中十分常见,如下图所示联系人列表。

在这里插入图片描述
对应代码

import router from '@ohos.router'
import { CodeView } from '../../../widget/CodeView'interface ContactGroup {title:stringcontacts:Array<String>
}@Entry
@Component
struct GroupListPage {contactsGroups: ContactGroup[] = [{title: 'A',contacts: ["安以轩","安悦溪",        ],},{title: 'B',contacts: ["白敬亭","白宇",       ],},...}]@Builder itemHead(text: string) {// 列表分组的头部组件,对应联系人分组A、B等位置的组件Text(text).fontSize(20).width('100%').padding(10).backgroundColor('#ffffff').fontWeight(FontWeight.Bold)}@Builder itemContent(text: string) {// 列表分组的头部组件,对应联系人分组A、B等位置的组件Text(text).padding({ left: 10, bottom: 10, top: 10 })}build() {Navigation() {List() {ForEach(this.contactsGroups, (item:ContactGroup)=>{ListItemGroup({ header: this.itemHead(item.title) }) {ForEach(item.contacts, (name:string)=> {ListItem() {this.itemContent(name)}})}})}}.title('支持分组列表').titleMode(NavigationTitleMode.Mini)}
}

添加粘性标题

运行效果
在这里插入图片描述
对应代码

List() {...
}
.sticky(StickyStyle.Header)  // 设置吸顶,实现粘性标题效果

控制滚动位置

在这里插入图片描述

对应代码

@Entry
@Component
struct ListScrollToPage {@State listItems:Array<String> = []private listScroller: Scroller = new Scroller();aboutToAppear() {for (var i =0;i< 50;i++) {this.listItems.push(`新闻${i+1}`)}}build() {Navigation() {Stack({ alignContent: Alignment.BottomEnd }) {List({ space: 5, scroller: this.listScroller }) {ForEach(this.listItems, (text: string) => {ListItem() {Text(text).textAlign(TextAlign.Center).width('100%').height(250).backgroundColor('#9dc3e6')}})}Image('image/scroll_to_top.svg').width(50).height(50).margin({right: 10,bottom: 10}).onClick(()=> {this.listScroller.scrollToIndex(0)})}}.title('控制滚动位置').titleMode(NavigationTitleMode.Mini)}
}

响应滚动位置

在这里插入图片描述
对应代码

Stack() {List() {...}.onScrollIndex((start, end)=> {this.firstIndex = start})Text(`当前第一个index:${this.firstIndex}`)...
}

响应列表项侧滑

在这里插入图片描述
对应代码

@Entry
@Component
struct SwipeListPage {@State listItems:Array<String> = []aboutToAppear() {for (var i =0;i< 50;i++) {this.listItems.push(`选项${i+1}`)}}@Builder itemEnd(index: number) {// 侧滑后尾端出现的组件Image("image/delete.svg").width(20).height(20).onClick(() => {this.listItems.splice(index, 1);})}build() {Navigation() {List({space: 5}) {ForEach(this.listItems, (item, index)=> {ListItem() {Text(item).textAlign(TextAlign.Center).width('100%').height(50).backgroundColor('#9dc3e6')}.swipeAction({ end: this.itemEnd.bind(this,index) })})}}.title('左滑删除列表').titleMode(NavigationTitleMode.Mini)}
}

给列表项添加标记

添加标记是一种无干扰性且直观的方法,用于显示通知或将注意力集中到应用内的某个区域。例如,当消息列表接收到新消息时,通常对应的联系人头像的右上方会出现标记,提示有若干条未读消息,如下图所示。
在这里插入图片描述

@Entry
@Component
struct BadgeListPage {@State listItems:Array<String> = []aboutToAppear() {for (var i =0;i< 50;i++) {this.listItems.push(`Item${i+1}`)}}build() {Navigation() {List({space: 5}) {ForEach(this.listItems, (item:string)=> {ListItem() {Row() {// 展示未读数Badge({count: 1,position: BadgePosition.RightTop,style: { badgeSize: 16, badgeColor: '#FA2A2D' }}) {// 未读数的头像Button().width(80).height(80).border({radius:90})}.margin({left:15})// 右侧文字Text(item).fontColor(Color.White).width('100%').height(100).padding({left:20})}}})}.backgroundColor(Color.Gray)}.title('列表项添加标记').titleMode(NavigationTitleMode.Mini)}
}

长列表的处理

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

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

@Entry
@Component
struct LazyForEachPage {dataSource:MyDataSource = new MyDataSource();aboutToAppear() {for (var i =0;i< 50;i++) {this.dataSource.pushData(`Item${i+1}`)}}build() {Navigation() {List({space: 5}) {LazyForEach(this.dataSource, (item:string)=> {ListItem() {Text(item).width('100%').height(100).backgroundColor('#9dc3e6')}})}}.title('LazyForEach 列表').titleMode(NavigationTitleMode.Mini)}
}// Basic implementation of IDataSource to handle data listener
class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] = [];getData(index: number) {return ""}totalCount(): number {return 0}// 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {console.info('add listener');this.listeners.push(listener);}}// 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);if (pos >= 0) {console.info('remove listener');this.listeners.splice(pos, 1);}}// 通知LazyForEach组件需要重载所有子组件notifyDataReload(): void {this.listeners.forEach(listener => {listener.onDataReloaded();})}// 通知LazyForEach组件需要在index对应索引处添加子组件notifyDataAdd(index: number): void {this.listeners.forEach(listener => {listener.onDataAdd(index);})}// 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件notifyDataChange(index: number): void {this.listeners.forEach(listener => {listener.onDataChange(index);})}// 通知LazyForEach组件需要在index对应索引处删除该子组件notifyDataDelete(index: number): void {this.listeners.forEach(listener => {listener.onDataDelete(index);})}
}class MyDataSource extends BasicDataSource {private dataArray: string[] = [];totalCount(): number {return this.dataArray.length;}getData(index: number) {return this.dataArray[index];}public addData(index: number, data: string): void {this.dataArray.splice(index, 0, data);this.notifyDataAdd(index);}public pushData(data: string): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}

上一篇 2.6、媒体查询(mediaquery)
下一篇 2.8、下拉刷新与上拉加载

相关文章:

  • 【LeetCode】1. 两数之和(Java自用版)
  • 我在京东做数据分析,一位京东数据分析师的工作日常
  • 一篇复现Docker镜像操作与容器操作
  • 机器学习:处理jira工单的分类问题
  • 复盘一下我用过的设计模式
  • 如何忽略Chrome最小字号的限制
  • 机器学习神经网络由哪些构成?
  • 计算机网络:现代通信的基石
  • 阿里云倚天云服务器怎么样?如何收费?
  • WPF---1.入门学习
  • 生产计划数据模型,实现能源企业数字化高效管理
  • FastAPI+React全栈开发05 React前端框架概述
  • 时序预测 | Matlab实现SSA-BP麻雀算法优化BP神经网络时间序列预测
  • SQLite3 数据库的基本操作
  • docker-面试题
  • 【前端学习】-粗谈选择器
  • 〔开发系列〕一次关于小程序开发的深度总结
  • Angular2开发踩坑系列-生产环境编译
  • leetcode-27. Remove Element
  • Next.js之基础概念(二)
  • PHP面试之三:MySQL数据库
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • TypeScript实现数据结构(一)栈,队列,链表
  • 初识MongoDB分片
  • 对象管理器(defineProperty)学习笔记
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 分享一个自己写的基于canvas的原生js图片爆炸插件
  • 给初学者:JavaScript 中数组操作注意点
  • 回顾 Swift 多平台移植进度 #2
  • 基于OpenResty的Lua Web框架lor0.0.2预览版发布
  • 将回调地狱按在地上摩擦的Promise
  • 解决iview多表头动态更改列元素发生的错误
  • 算法系列——算法入门之递归分而治之思想的实现
  • 由插件封装引出的一丢丢思考
  • 做一名精致的JavaScripter 01:JavaScript简介
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • ​iOS实时查看App运行日志
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • $(function(){})与(function($){....})(jQuery)的区别
  • (02)Hive SQL编译成MapReduce任务的过程
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (算法设计与分析)第一章算法概述-习题
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • (转)IOS中获取各种文件的目录路径的方法
  • .NET : 在VS2008中计算代码度量值
  • .NET Core 中的路径问题
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .NET/C# 在 64 位进程中读取 32 位进程重定向后的注册表
  • .net安装_还在用第三方安装.NET?Win10自带.NET3.5安装
  • .NET导入Excel数据
  • .NET多线程执行函数
  • .Net下使用 Geb.Video.FFMPEG 操作视频文件