如何實(shí)現(xiàn)列表項(xiàng)的新增和刪除—OpenHarmony

想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):
場(chǎng)景介紹
列表的編輯模式用途十分廣泛,常見(jiàn)于待辦事項(xiàng)管理、文件管理、備忘錄的記錄管理等應(yīng)用場(chǎng)景。在列表的編輯模式下,新增和刪除列表項(xiàng)是最基礎(chǔ)的功能,其核心是對(duì)列表項(xiàng)對(duì)應(yīng)的數(shù)據(jù)集合進(jìn)行數(shù)據(jù)添加和刪除。
下面以待辦事項(xiàng)管理為例,介紹如何快速實(shí)現(xiàn)新增和刪除列表項(xiàng)功能。
環(huán)境要求
本例基于以下環(huán)境開(kāi)發(fā),開(kāi)發(fā)者也可以基于其他適配的版本進(jìn)行開(kāi)發(fā):
- IDE: DevEco Studio 3.1 Release
- SDK: Ohos_sdk_public 3.2.12.5(API Version 9 Release)
新增列表項(xiàng)
如下圖所示,當(dāng)用戶點(diǎn)擊添加按鈕時(shí),將彈出列表項(xiàng)選擇界面,用戶點(diǎn)擊確定后,列表中新增對(duì)應(yīng)項(xiàng)目。

圖17 新增待辦
開(kāi)發(fā)步驟
定義列表項(xiàng)數(shù)據(jù)結(jié)構(gòu)和初始化列表數(shù)據(jù),構(gòu)建列表整體布局和列表項(xiàng)。
以待辦事項(xiàng)管理為例,首先定義待辦事項(xiàng)的數(shù)據(jù)結(jié)構(gòu):
import util from '@ohos.util';
export class ToDo {
key: string = util.generateRandomUUID(true);
name: string;
constructor(name: string) {
this.name = name;
}
}然后,初始化待辦事項(xiàng)列表和可選事項(xiàng)列表:
@State toDoData: ToDo[] = [];
private availableThings: string[] = ['讀書(shū)', '運(yùn)動(dòng)', '旅游', '聽(tīng)音樂(lè)', '看電影', '唱歌'];構(gòu)建UI界面。
初始界面包含“待辦”和新增按鈕“+”:
Text('待辦')
.fontSize(36)
.margin({ left: 40})
Blank()
Text('+')
.fontWeight(FontWeight.Lighter)
.fontSize(40)
.margin({ right: 30 })構(gòu)建列表布局并通過(guò)ForEach循環(huán)渲染列表項(xiàng):
List({ space: 10 }) {
ForEach(this.toDoData, (toDoItem) => {
ListItem() {
...
}
}, toDoItem => toDoItem.key)
}為新增按鈕綁定點(diǎn)擊事件,并在事件中通過(guò)TextPickerDialog.show添加新增列表項(xiàng)的邏輯:
Text('+')
.onClick(() => {
TextPickerDialog.show({
range: this.availableThings, // 將可選事項(xiàng)列表配置到選擇對(duì)話框中
onAccept: (value: TextPickerResult) => {
this.toDoData.push(new ToDo(this.availableThings[value.index])); // 用戶點(diǎn)擊確認(rèn),將選擇的數(shù)據(jù)添加到待辦列表toDoData中
},
})
})刪除列表項(xiàng)
如下圖所示,當(dāng)用戶長(zhǎng)按列表項(xiàng)進(jìn)入刪除模式時(shí),提供用戶刪除列表項(xiàng)選擇的交互界面,用戶勾選完成后點(diǎn)擊刪除按鈕,列表中刪除對(duì)應(yīng)的項(xiàng)目。

圖18 長(zhǎng)按刪除待辦事項(xiàng)
開(kāi)發(fā)步驟
列表的刪除功能一般進(jìn)入編輯模式后才可使用,所以需要提供編輯模式的入口。
以待辦列表為例,通過(guò)LongPressGesture()監(jiān)聽(tīng)列表項(xiàng)的長(zhǎng)按事件,當(dāng)用戶長(zhǎng)按列表項(xiàng)時(shí),進(jìn)入編輯模式。
// ToDoListItem.ets
Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
...
}
.gesture(
GestureGroup(GestureMode.Exclusive,
LongPressGesture() // 監(jiān)聽(tīng)長(zhǎng)按事件
.onAction(() => {
if (!this.isEditMode) {
this.isEditMode = true; //進(jìn)入編輯模式
this.selectedItems.push(this.toDoItem); // 記錄長(zhǎng)按時(shí)選中的列表項(xiàng)
}
})
)
)需要響應(yīng)用戶的選擇交互,記錄要?jiǎng)h除的列表項(xiàng)數(shù)據(jù)。
在待辦列表中,通過(guò)勾選框的勾選或取消勾選,響應(yīng)用戶勾選列表項(xiàng)變化,記錄所有選擇的列表項(xiàng)。
// ToDoListItem.ets
if (this.isEditMode) {
Checkbox()
.onChange((isSelected) => {
if (isSelected) {
this.selectedItems.push(this.toDoItem) // 勾選時(shí),記錄選中的列表項(xiàng)
} else {
let index = this.selectedItems.indexOf(this.toDoItem)
if (index !== -1) {
this.selectedItems.splice(index, 1) // 取消勾選時(shí),則將此項(xiàng)從selectedItems中刪除
}
}
})
...
}需要響應(yīng)用戶點(diǎn)擊刪除按鈕事件,刪除列表中對(duì)應(yīng)的選項(xiàng)。
// ToDoList.ets
Button('刪除')
.onClick(() => {
// 刪除選中的列表項(xiàng)對(duì)應(yīng)的toDoData數(shù)據(jù)
let leftData = this.toDoData.filter((item) => {
return this.selectedItems.find((selectedItem) => selectedItem !== item);
})
this.toDoData = leftData;
this.isEditMode = false;
})
...完整示例代碼
新增和刪除列表項(xiàng)的實(shí)現(xiàn)共涉及三個(gè)文件,各文件完整代碼如下:
待辦事項(xiàng)數(shù)據(jù)結(jié)構(gòu)代碼(ToDo.ets):
// ToDo.ets
import util from '@ohos.util';
export class ToDo {
key: string = util.generateRandomUUID(true)
name: string;
constructor(name: string) {
this.name = name;
}
}待辦事項(xiàng)列表代碼(ToDoList.ets):
// ToDoList.ets
import { ToDo } from '../model/ToDo';
import { ToDoListItem } from './ToDoListItem';
@Entry
@Component
struct ToDoList {
@State toDoData: ToDo[] = []
@Watch('onEditModeChange') @State isEditMode: boolean = false
@State selectedItems: ToDo[] = []
private availableThings: string[] = ["讀書(shū)", "運(yùn)動(dòng)", "旅游", '聽(tīng)音樂(lè)', '看電影', '唱歌']
saveData(value: string) {
this.toDoData.push(new ToDo(value))
}
onEditModeChange() {
if (!this.isEditMode) {
this.selectedItems = []
}
}
build() {
Column() {
Row() {
if (this.isEditMode) {
Text('X')
.fontSize(20)
.onClick(() => {
this.isEditMode = false;
})
.margin({ left: 20, right: 20 })
Text('已選擇' + this.selectedItems.length + '項(xiàng)')
.fontSize(24)
} else {
Text('待辦')
.fontSize(36)
.margin({ left: 40})
Blank()
Text('+')
.fontWeight(FontWeight.Lighter)
.fontSize(40)
.margin({ right: 30 })
.onClick(() => {
TextPickerDialog.show({
range: this.availableThings,
onAccept: (value: TextPickerResult) => {
this.toDoData.push(new ToDo(this.availableThings[value.index]))
console.info('to do data: ' + JSON.stringify(this.toDoData))
},
})
})
}
}
.height('12%')
.width('100%')
List({ initialIndex: 0, space: 10 }) {
ForEach(this.toDoData, toDoItem => {
ListItem() {
ToDoListItem({
isEditMode: $isEditMode,
toDoItem: toDoItem,
selectedItems: $selectedItems
})
}.padding({ left: 24, right: 24, bottom: 12 })
}, toDoItem => toDoItem.key)
}
.height('73%')
.listDirection(Axis.Vertical)
.edgeEffect(EdgeEffect.Spring)
if (this.isEditMode) {
Row() {
Button('刪除')
.width('80%')
.onClick(() => {
let leftData = this.toDoData.filter((item) => {
return this.selectedItems.find((selectedItem) => selectedItem != item)
})
console.log('leftData: ' + leftData);
this.isEditMode = false;
this.toDoData = leftData;
})
.backgroundColor('#ffd75d5d')
}
.height('15%')
}
}
.backgroundColor('#fff1f3f5')
.width('100%')
.height('100%')
}
}待辦事項(xiàng)代碼(ToDoListItem.ets):
// ToDoListItem.ets
import { ToDo } from '../model/ToDo';
@Component
export struct ToDoListItem {
@Link isEditMode: boolean
@Link selectedItems: ToDo[]
private toDoItem: ToDo;
hasBeenSelected(): boolean {
return this.selectedItems.indexOf(this.toDoItem) != -1
}
build() {
Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
Row({ space: 4 }) {
Circle()
.width(24)
.height(24)
.fill(Color.White)
.borderWidth(3)
.borderRadius(30)
.borderColor('#ffdcdfdf')
.margin({ right: 10 })
Text(`${this.toDoItem.name}`)
.maxLines(1)
.fontSize(24)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
.padding({ left: 12 })
if (this.isEditMode) {
Checkbox()
.select(this.hasBeenSelected() ? true : false)
.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)
}
}
})
.width(24)
.height(24)
}
}
.width('100%')
.height(80)
.padding({
left: 16,
right: 12,
top: 4,
bottom: 4
})
.borderRadius(24)
.linearGradient({
direction: GradientDirection.Right,
colors: this.hasBeenSelected() ? [[0xffcdae, 0.0], [0xFfece2, 1.0]] : [[0xffffff, 0.0], [0xffffff, 1.0]]
})
.gesture(
GestureGroup(GestureMode.Exclusive,
LongPressGesture()
.onAction(() => {
if (!this.isEditMode) {
this.isEditMode = true
this.selectedItems.push(this.toDoItem)
}
})
)
)
}
}































