List列表組件基礎展示與數據綁定
引言
在HarmonyOS應用開發中,List組件是最常用且功能強大的數據展示控件之一。無論你是開發社交應用的消息列表、電商應用的商品列表,還是設置頁面的選項列表,List組件都能提供流暢的滾動體驗和高效的性能表現。本文將帶你從零開始掌握List組件的使用,重點講解基礎展示、數據綁定以及實際開發中的最佳實踐。
官方參考資料:
- List組件官方文檔
- List組件api
- ArkTS語言官方文檔
一、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!