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:string
    contacts: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、下拉刷新与上拉加载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/487924.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Apache Spark

一、Apache Spark 1、Spark简介 Apache Spark是用于大规模数据 (large-scala data) 处理的统一 (unified) 分析引擎。 Spark官网 Spark最早源于一篇论文Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing,该论文是由加州大学柏…

Harmony OS 网络编程 实验指南

netcat简介 netcat 是什么&#xff1f; netcat是一个非常强大的网络实用工具&#xff0c;可以用它来调试TCP/UDP应用程序&#xff1b; netcat 如何安装&#xff1f; Linux上可以使用发行版的包管理器安装&#xff0c;例如Debian/Ubuntu上&#xff1a; sudo apt-get instal…

cas学习2:idea里搭建cas项目

在上篇中介绍了cas服务在tomcat中怎么启动的及某j集成cas&#xff0c;这篇讲下idea怎么集成cas成一个项目&#xff0c;为后续的定制化开发做好铺垫。 1.下载CAS 模板 Overlay Template&#xff0c;我这里使用 Apereo CAS 5.3 版本&#xff0c;JDK需要1.8 地址&#xff1a;Git…

钡铼技术R40路由器助力构建无人值守的智能化污水处理厂

钡铼技术R40路由器作为智能化污水处理厂的关键网络设备&#xff0c;发挥着至关重要的作用&#xff0c;助力构建无人值守的智能化污水处理系统。在现代社会&#xff0c;污水处理是城市环境保护和可持续发展的重要组成部分&#xff0c;而智能化污水处理厂借助先进的技术和设备&am…

微信小程序wx.navigateTo无法跳转到Component组件问题解决。(共享元素动画必备)

关于Component构造器官方是有文档说明的&#xff0c;然后官方文档内部也给出了组件是可以通过像pages一样跳转的。但是官方文档缺少了必要的说明&#xff0c;会引起wx.navigateTo无法跳转到组件问题&#xff01; 以下是官方文档截图&#xff1a; 解决方式&#xff1a; 组件创建…

4.2 循环语句loop,等差数列求和

汇编语言 1. 循环语句loop loop指令的格式是&#xff1a;loop 标号&#xff0c;CPU执行loop指令的时候&#xff0c;要进行两部操作 cx cx - 1;判断cx中的值&#xff0c;不为0则转至标号处执行程序&#xff0c;如果为0则向下执行 循环使用loop来实现&#xff0c;循环次数存…

flask_restful的基本使用

优势&#xff1a; Flask-Restful 是一个专门用来写 restful api 的一个插件。 使用它可以快速的集成restful api 接口功能。 在系统的纯api 的后台中&#xff0c;这个插件可以帮助我们节省很多时间。 缺点&#xff1a; 如果在普通的网站中&#xff0c;这个插件就没有优势了&…

技术文件分享 | 《基于倾斜摄影测量的城市级实景三维地理场景模型生产技术规程》.pdf

为提高利用倾斜摄影测量技术生产城市级实景三维地理场景模型成果水平,湖北省地理国情监测中心联合武汉大学、武汉大势智慧科技有限公司等单位制定了《基于倾斜摄影测量的城市级实景三维地理场景模型生产技术规程》&#xff0c;经湖北省市场监管局批准、备案&#xff0c;被收录为…

如何调用occtproxy放入自己的wpf文件

1.创建一个wpf程序 2.添加项目occtproxy.vcxproj 3.把该项目配置类型设为dll 4.添加引用 5.报错显示&#xff0c;这是因为还没有生成dll 6.把occtproxy设为启动项目运行&#xff0c;设定输出目录在该目录下&#xff0c;生成dll 7.再运行&#xff0c;即可

Oracle 控制文件详解

1、控制文件存储的数据信息 1&#xff09;数据库名称和数据库唯一标识符&#xff08;DBID) 2&#xff09;创建数据库的时间戳 3&#xff09;有关数据文件、联机重做日志文件、归档重做日志文件的信息 4&#xff09;表空间信息 5&#xff09;检查点信息 6&#xff09;日志序列号…

tcp和udp分别是什么?udp和tcp的区别

TCP和UDP是计算机网络中常见的两种传输层协议&#xff0c;它们在实际应用中具有不同的特点和用途。本文将对TCP和UDP进行介绍&#xff0c;并分析它们之间的区别。 TCP和UDP分别是什么&#xff1f; TCP&#xff08;Transmission Control Protocol&#xff09; TCP是一种面向连…

Spring Boot | SpringBoo“开发入门“

目录 : 1.SpringBoot的“介绍”SpringBoot”概述” &#xff1a;SpringBoot”简介“SpringBoot的“优点” 2. SpringBoot入门程序环境准备使用 “Maven”方式构建SpringBoot 项目使用“Spring Initializr”方式构建Spring Boot 项目 3. “单元测试” 和“热部署”单元测试热部署…

微服务day07 -- 搜索引擎 ( 数据聚合 + 自动补全 + 数据同步 + ES集群 )

1.数据聚合 聚合&#xff08;aggregations&#xff09;可以让我们极其方便的实现对数据的统计、分析、运算。例如&#xff1a; 什么品牌的手机最受欢迎&#xff1f; 这些手机的平均价格、最高价格、最低价格&#xff1f; 这些手机每月的销售情况如何&#xff1f; 实现这些…

基于springboot的美食分享管理平台+数据库+部署文档+数据库表结构文档+免费远程调试

项目介绍: 基于springboot的美食分享管理平台。Javaee项目&#xff0c;springboot项目&#xff0c;采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过SpringBoot MybatisBootstrap来实现。MyS…

Facebook是什么?有什么功能?如何利用Facebook运营?

Facebook&#xff0c;也常被人们称为“脸书”、“脸谱”等&#xff0c;是美国的社交网络服务及社会化媒体网站&#xff0c;拥有超过20亿的月活跃用户&#xff0c;对于众多商家而言&#xff0c;Facebook以其广泛的用户基础和强大的社交影响力&#xff0c;成为了一个理想的社媒营…

http和socks5代理哪个隐蔽性更强?

HTTP代理和SOCKS5代理各有其优缺点&#xff0c;但就隐蔽性而言&#xff0c;SOCKS5代理通常比HTTP代理更隐蔽。以下是它们的比较&#xff1a; HTTP代理&#xff1a; 透明性较高&#xff1a;HTTP代理在HTTP头中会透露原始客户端的IP地址&#xff0c;这使得它相对不太隐蔽。…

JetPack之Room入门

目录 一、简介1.1 主要组件1.2 三者关系 二、基础使用2.1 依赖导入2.2 Student 实体类定义2.3 StudentDao 操作接口类2.4 StudentDataBase 类2.5 RoomTestActivity 类 三、参考链接 一、简介 JetPack 中的 Room 是一个用于在 SQLite 数据库上提供抽象层的持久性库。它允许开发…

MySQL之ACID实现原理

(/≧▽≦)/~┴┴ 嗨~我叫小奥 ✨✨✨ &#x1f440;&#x1f440;&#x1f440; 个人博客&#xff1a;小奥的博客 &#x1f44d;&#x1f44d;&#x1f44d;&#xff1a;个人CSDN ⭐️⭐️⭐️&#xff1a;Github传送门 &#x1f379; 本人24应届生一枚&#xff0c;技术和水平有…

政安晨:【TensorFlow与Keras实战演绎机器学习】专栏 —— 目录

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras实战演绎机器学习 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 本篇是作者政安晨的专栏《TensorFlow与Keras…

Appium Inspector 展示设备当前页面

定位元素需要使用appium inspector&#xff0c;之前每次都是从登录页开始&#xff0c;后来发现连接设备的时候只需要去掉appPackage、appActivity即可。 { "platformName": "Android", "platformVersion": "6", "deviceNa…