鸿蒙学习实战之路:List列表组件基础展示与数据绑定

List列表组件基础展示与数据绑定

引言

在HarmonyOS应用开发中,List组件是最常用且功能强大的数据展示控件之一。无论你是开发社交应用的消息列表、电商应用的商品列表,还是设置页面的选项列表,List组件都能提供流畅的滚动体验和高效的性能表现。本文将带你从零开始掌握List组件的使用,重点讲解基础展示、数据绑定以及实际开发中的最佳实践。

官方参考资料

一、List组件基础概念

1.1 什么是List组件

List组件是一个可滚动的列表容器,用于呈现相同或相似类型的数据集合。它具有以下核心特性:

  • 垂直滚动:默认支持垂直方向的滚动
  • 高性能:采用懒加载机制,只渲染可视区域内的项
  • 多样化布局:支持线性布局、网格布局等多种排列方式
  • 丰富的交互:支持点击、长按、滑动等手势操作

1.2 List组件的基本结构

一个典型的List组件包含以下部分:

List() {
  // 列表项内容
  ListItem() {
    // 列表项的具体UI组件
  }
}

二、创建基础List列表

2.1 最简单的List示例

让我们从一个最基本的文本列表开始:

@Entry
@Component
struct BasicListExample {
  build() {
    Column() {
      List({ space: 10 }) {
        ListItem() {
          Text('列表项1')
            .fontSize(20)
            .padding(10)
        }

        ListItem() {
          Text('列表项2')
            .fontSize(20)
            .padding(10)
        }

        ListItem() {
          Text('列表项3')
            .fontSize(20)
            .padding(10)
        }
      }
      .width('100%')
      .height('100%')
    }
  }
}

2.2 List核心属性详解

List组件提供了丰富的属性来控制列表的显示和行为:

属性名 类型 说明 默认值
space number 列表项之间的间距 0
initialIndex number 初始滚动位置 0
scroller Scroller 列表滚动控制器 -
listDirection Axis 列表方向(垂直/水平) Axis.Vertical

重要提示:在实际开发中,建议始终设置List的宽度和高度,否则可能无法正常显示滚动效果。

三、数据绑定与动态列表

3.1 使用ForEach进行数据绑定

静态定义的列表在实际开发中很少使用,大多数情况下我们需要动态绑定数据:

@Entry
@Component
struct DynamicListExample {
  // 定义数据源
  private items: string[] = ['苹果', '香蕉', '橙子', '葡萄', '西瓜', '芒果']

  build() {
    Column() {
      List({ space: 8 }) {
        // 使用ForEach循环渲染列表项
        ForEach(this.items, (item: string, index: number) => {
          ListItem() {
            Text(`${index + 1}. ${item}`)
              .fontSize(18)
              .fontColor(Color.Black)
              .backgroundColor(Color.White)
              .padding(12)
              .width('100%')
          }
        }, (item: string) => item)
      }
      .width('100%')
      .height('100%')
      .divider({ strokeWidth: 1, color: '#F1F1F1' })
    }
  }
}

3.2 复杂数据结构的列表展示

实际应用中的数据通常更复杂,让我们看一个对象数组的示例:

// 定义数据模型
class Product {
  id: number
  name: string
  price: number
  category: string

  constructor(id: number, name: string, price: number, category: string) {
    this.id = id
    this.name = name
    this.price = price
    this.category = category
  }
}

@Entry
@Component
struct ProductListExample {
  private products: Product[] = [
    new Product(1, '华为Mate 60', 5999, '手机'),
    new Product(2, '华为Watch 4', 2499, '智能手表'),
    new Product(3, '华为MatePad', 3299, '平板电脑'),
    new Product(4, '华为FreeBuds', 899, '耳机')
  ]

  build() {
    Column() {
      List({ space: 12 }) {
        ForEach(this.products, (product: Product) => {
          ListItem() {
            Column({ space: 8 }) {
              Text(product.name)
                .fontSize(20)
                .fontColor('#0018A7')
                .fontWeight(FontWeight.Bold)
                .width('100%')

              Row() {
                Text(`¥${product.price}`)
                  .fontSize(16)
                  .fontColor('#E30000')

                Text(product.category)
                  .fontSize(14)
                  .fontColor('#666666')
                  .margin({ left: 12 })
                  .backgroundColor('#F5F5F5')
                  .padding({ left: 8, right: 8, top: 2, bottom: 2 })
                  .borderRadius(4)
              }
              .width('100%')
              .justifyContent(FlexAlign.Start)
            }
            .padding(16)
            .backgroundColor(Color.White)
            .borderRadius(8)
            .shadow({ radius: 4, color: '#1A000000', offsetX: 0, offsetY: 2 })
          }
        }, (product: Product) => product.id.toString())
      }
      .padding(16)
      .width('100%')
      .height('100%')
      .backgroundColor('#F8F8F8')
    }
  }
}

注意事项

  • ForEach的第三个参数是键值生成函数,必须为每个列表项提供唯一的键值
  • 对于对象数组,建议使用对象的唯一标识符作为键值
  • 如果没有提供合适的键值,列表的更新性能会受到影响

四、列表交互与事件处理

4.1 点击事件处理

为列表项添加点击交互:

@Entry
@Component
struct InteractiveListExample {
  private messages: string[] = [
    '早上好,今天天气不错',
    '下午有个重要的会议',
    '记得完成项目报告',
    '晚上7点健身房见'
  ]

  build() {
    Column() {
      List({ space: 1 }) {
        ForEach(this.messages, (message: string, index: number) => {
          ListItem() {
            Row() {
              Text(`消息 ${index + 1}`)
                .fontSize(16)
                .fontWeight(FontWeight.Medium)
                
              Text(message)
                .fontSize(14)
                .fontColor('#666666')
                .maxLines(1)
                .textOverflow({ overflow: TextOverflow.Ellipsis })
                .layoutWeight(1)
            }
            .padding(16)
            .width('100%')
          }
          .onClick(() => {
            // 处理点击事件
            console.log(`点击了第${index + 1}条消息: ${message}`)
            // 在实际应用中,这里通常会跳转到详情页面或执行其他操作
          })
        }, (message: string, index: number) => index.toString())
      }
      .width('100%')
      .height('100%')
    }
  }
}

4.2 长按事件与滑动操作

@Entry
@Component
struct AdvancedInteractionList {
  @State private tasks: string[] = [
    '完成HarmonyOS学习',
    '编写项目文档',
    '代码评审',
    '团队会议',
    '产品需求分析'
  ]

  build() {
    Column() {
      List({ space: 8 }) {
        ForEach(this.tasks, (task: string, index: number) => {
          ListItem() {
            Text(task)
              .fontSize(18)
              .padding(16)
              .width('100%')
              .backgroundColor(Color.White)
              .borderRadius(8)
          }
          .onClick(() => {
            console.log(`开始任务: ${task}`)
          })
          .onLongPress(() => {
            console.log(`长按任务: ${task}`)
            // 显示删除确认对话框等
          })
          .swipeAction({ 
            end: this.DeleteButton(index) 
          })
        }, (task: string, index: number) => index.toString())
      }
      .width('100%')
      .height('100%')
      .padding(16)
      .backgroundColor('#F5F5F5')
    }
  }

  @Builder DeleteButton(index: number) {
    Button('删除')
      .backgroundColor(Color.Red)
      .fontColor(Color.White)
      .width(80)
      .height('100%')
      .onClick(() => {
        // 删除对应的任务
        this.tasks.splice(index, 1)
        this.tasks = [...this.tasks] // 触发UI更新
      })
  }
}

五、自定义列表项布局

5.1 复杂列表项设计

实际应用中的列表项通常包含图片、文本等多种元素:

class Contact {
  id: string
  name: string
  phone: string
  avatar: Resource
  online: boolean

  constructor(id: string, name: string, phone: string, avatar: Resource, online: boolean) {
    this.id = id
    this.name = name
    this.phone = phone
    this.avatar = avatar
    this.online = online
  }
}

@Entry
@Component
struct ContactListExample {
  private contacts: Contact[] = [
    new Contact('1', '张三', '138****1234', $r('app.media.avatar1'), true),
    new Contact('2', '李四', '139****5678', $r('app.media.avatar2'), false),
    new Contact('3', '王五', '136****9012', $r('app.media.avatar3'), true)
  ]

  build() {
    Column() {
      List({ space: 1 }) {
        ForEach(this.contacts, (contact: Contact) => {
          ListItem() {
            Row({ space: 12 }) {
              // 头像
              Stack() {
                Image(contact.avatar)
                  .width(50)
                  .height(50)
                  .borderRadius(25)
                
                // 在线状态指示器
                if (contact.online) {
                  Circle({ width: 12, height: 12 })
                    .fill('#52C41A')
                    .position({ x: 38, y: 38 })
                    .border({ width: 2, color: Color.White })
                }
              }
              .width(50)
              .height(50)

              // 联系人信息
              Column({ space: 4 }) {
                Text(contact.name)
                  .fontSize(18)
                  .fontColor(Color.Black)
                  .fontWeight(FontWeight.Medium)
                  .width('100%')

                Text(contact.phone)
                  .fontSize(14)
                  .fontColor('#666666')
                  .width('100%')
              }
              .layoutWeight(1)

              // 操作按钮
              Image($r('app.media.ic_call'))
                .width(24)
                .height(24)
                .onClick(() => {
                  console.log(`呼叫 ${contact.name}`)
                })
            }
            .padding(16)
            .width('100%')
          }
        }, (contact: Contact) => contact.id)
      }
      .width('100%')
      .height('100%')
    }
  }
}

六、列表性能优化

6.1 使用LazyForEach处理大数据集

当列表数据量很大时,使用LazyForEach可以显著提升性能:

// 数据源实现接口
class MyDataSource implements IDataSource {
  private data: string[] = []
  private listeners: DataChangeListener[] = []

  constructor(data: string[]) {
    this.data = data
  }

  totalCount(): number {
    return this.data.length
  }

  getData(index: number): string {
    return this.data[index]
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    this.listeners.push(listener)
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {
    const index = this.listeners.indexOf(listener)
    if (index >= 0) {
      this.listeners.splice(index, 1)
    }
  }
}

@Entry
@Component
struct LargeListExample {
  private dataSource: MyDataSource = new MyDataSource(
    Array.from({ length: 1000 }, (_, i) => `列表项 ${i + 1}`)
  )

  build() {
    Column() {
      List({ space: 1 }) {
        LazyForEach(this.dataSource, (item: string) => {
          ListItem() {
            Text(item)
              .fontSize(16)
              .padding(12)
              .width('100%')
          }
        }, (item: string) => item)
      }
      .width('100%')
      .height('100%')
    }
  }
}

6.2 列表性能优化技巧

  • 使用合适的键值:为每个列表项提供稳定且唯一的键值
  • 避免复杂布局:简化列表项的布局层次
  • 图片优化:对列表中的图片进行适当压缩和缓存
  • 分页加载:大数据集采用分页加载策略
  • 虚拟化渲染:List组件默认支持,无需额外配置

七、实际开发中的注意事项

7.1 常见问题与解决方案

问题1:列表滚动卡顿

  • 原因:列表项布局过于复杂或数据绑定效率低
  • 解决方案:
    • 使用LazyForEach替代ForEach处理大数据集
    • 简化列表项布局结构
    • 避免在列表项中使用过于复杂的计算

问题2:列表项状态异常

  • 原因:键值不唯一或状态管理不当
  • 解决方案:
    • 确保每个列表项有唯一的键值
    • 合理使用@State、@Prop等装饰器管理状态

问题3:内存泄漏

  • 原因:事件监听器未正确移除
  • 解决方案:
    • 及时移除不需要的事件监听
    • 使用弱引用或适当的作用域管理

7.2 版本兼容性说明

  • List组件从API version 7开始支持
  • swipeAction属性从API version 9开始支持
  • LazyForEach从API version 8开始支持
  • 建议在开发时关注目标设备的HarmonyOS版本

八、完整实战案例:任务管理应用

让我们结合所学知识,创建一个完整的任务管理应用:

class Task {
  id: string
  title: string
  completed: boolean
  priority: number // 1-高, 2-中, 3-低
  createTime: number

  constructor(id: string, title: string, priority: number) {
    this.id = id
    this.title = title
    this.completed = false
    this.priority = priority
    this.createTime = new Date().getTime()
  }
}

@Entry
@Component
struct TaskManagerApp {
  @State private tasks: Task[] = [
    new Task('1', '学习HarmonyOS开发', 1),
    new Task('2', '完成项目文档', 2),
    new Task('3', '购买生活用品', 3)
  ]

  @State private newTaskTitle: string = ''

  build() {
    Column() {
      // 标题
      Text('任务管理')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 20 })

      // 添加新任务
      Row({ space: 8 }) {
        TextInput({ placeholder: '输入新任务...', text: this.newTaskTitle })
          .layoutWeight(1)
          .onChange((value: string) => {
            this.newTaskTitle = value
          })

        Button('添加')
          .backgroundColor('#1890FF')
          .fontColor(Color.White)
          .onClick(() => {
            if (this.newTaskTitle.trim()) {
              const newTask = new Task(
                Date.now().toString(),
                this.newTaskTitle,
                2
              )
              this.tasks = [newTask, ...this.tasks]
              this.newTaskTitle = ''
            }
          })
      }
      .padding(16)
      .width('100%')

      // 任务列表
      List({ space: 8 }) {
        ForEach(this.tasks, (task: Task) => {
          ListItem() {
            this.TaskItem(task)
          }
          .swipeAction({ 
            end: this.DeleteTaskButton(task.id) 
          })
        }, (task: Task) => task.id)
      }
      .layoutWeight(1)
      .width('100%')
      .padding(16)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F8F9FA')
  }

  @Builder
  TaskItem(task: Task) {
    Row({ space: 12 }) {
      // 完成状态复选框
      Image(task.completed ? $r('app.media.ic_checked') : $r('app.media.ic_unchecked'))
        .width(24)
        .height(24)
        .onClick(() => {
          task.completed = !task.completed
          this.tasks = [...this.tasks]
        })

      // 任务内容
      Column({ space: 4 }) {
        Text(task.title)
          .fontSize(16)
          .fontColor(task.completed ? '#999999' : Color.Black)
          .decoration({ type: task.completed ? TextDecorationType.LineThrough : TextDecorationType.None })

        // 优先级标签
        Row() {
          Text(this.getPriorityText(task.priority))
            .fontSize(12)
            .fontColor(this.getPriorityColor(task.priority))
        }
        .padding({ left: 4, right: 4, top: 2, bottom: 2 })
        .backgroundColor(this.getPriorityBgColor(task.priority))
        .borderRadius(4)
      }
      .layoutWeight(1)
    }
    .padding(16)
    .width('100%')
    .backgroundColor(Color.White)
    .borderRadius(8)
    .shadow({ radius: 2, color: '#1A000000', offsetX: 0, offsetY: 1 })
  }

  @Builder
  DeleteTaskButton(taskId: string) {
    Button('删除')
      .backgroundColor(Color.Red)
      .fontColor(Color.White)
      .width(80)
      .height('100%')
      .onClick(() => {
        this.tasks = this.tasks.filter(task => task.id !== taskId)
      })
  }

  private getPriorityText(priority: number): string {
    switch (priority) {
      case 1: return '高优先级'
      case 2: return '中优先级'
      case 3: return '低优先级'
      default: return '普通'
    }
  }

  private getPriorityColor(priority: number): string {
    switch (priority) {
      case 1: return '#CF1322'
      case 2: return '#FA8C16'
      case 3: return '#52C41A'
      default: return '#666666'
    }
  }

  private getPriorityBgColor(priority: number): string {
    switch (priority) {
      case 1: return '#FFF1F0'
      case 2: return '#FFF7E6'
      case 3: return '#F6FFED'
      default: return '#F5F5F5'
    }
  }
}

总结

通过本文的学习,你应该已经掌握了:

  • List组件的基础用法和核心属性
  • 使用ForEach和LazyForEach进行数据绑定
  • 列表交互事件的处理方法
  • 复杂列表项的自定义布局设计
  • 列表性能优化的实用技巧
  • 实际开发中的注意事项和最佳实践

List组件是HarmonyOS应用开发中不可或缺的重要组件,熟练掌握它的使用将极大提升你的开发效率和应用的性能表现。建议在实际项目中多加练习,不断探索List组件的更多高级特性。

下一步学习建议

  • 学习Grid网格布局组件
  • 探索List与Navigation的组合使用
  • 了解自定义组件在列表中的应用
  • 研究列表的动画和过渡效果

Happy Coding!

需要参加鸿蒙认证的请点击 鸿蒙认证链接

posted @ 2025-11-26 17:04  时间煮鱼  阅读(12)  评论(0)    收藏  举报