1# 如何实现列表项的新增和删除 2 3## 场景介绍 4列表的编辑模式用途十分广泛,常见于待办事项管理、文件管理、备忘录的记录管理等应用场景。在列表的编辑模式下,新增和删除列表项是最基础的功能,其核心是对列表项对应的数据集合进行数据添加和删除。 5 6下面以待办事项管理为例,介绍如何快速实现新增和删除列表项功能。 7 8## 环境要求 9- IDE:DevEco Studio 3.1 Beta1 10- SDK:Ohos_sdk_public 3.2.11.9 (API Version 9 Release) 11 12## 新增列表项 13 14如下图所示,当用户点击添加按钮时,将弹出列表项选择界面,用户点击确定后,列表中新增对应项目。 15 16 **图17** 新增待办 17 18 19 20### 开发步骤 21 221. 定义列表项数据结构和初始化列表数据,构建列表整体布局和列表项。 23 以待办事项管理为例,首先定义待办事项的数据结构: 24 25 ```ts 26 import util from '@ohos.util'; 27 28 export class ToDo { 29 key: string = util.generateRandomUUID(true); 30 name: string; 31 32 constructor(name: string) { 33 this.name = name; 34 } 35 } 36 ``` 37 382. 然后,初始化待办事项列表和可选事项列表: 39 40 ```ts 41 @State toDoData: ToDo[] = []; 42 private availableThings: string[] = ['读书', '运动', '旅游', '听音乐', '看电影', '唱歌']; 43 ``` 44 453. 构建UI界面。 46 初始界面包含“待办”和新增按钮“+”: 47 ```ts 48 Text('待办') 49 .fontSize(36) 50 .margin({ left: 40}) 51 Blank() 52 Text('+') 53 .fontWeight(FontWeight.Lighter) 54 .fontSize(40) 55 .margin({ right: 30 }) 56 ``` 57 构建列表布局并通过ForEach循环渲染列表项: 58 59 ```ts 60 List({ space: 10 }) { 61 ForEach(this.toDoData, (toDoItem) => { 62 ListItem() { 63 ... 64 } 65 }, toDoItem => toDoItem.key) 66 } 67 ``` 68 694. 为新增按钮绑定点击事件,并在事件中通过TextPickerDialog.show添加新增列表项的逻辑: 70 71 ```ts 72 Text('+') 73 .onClick(() => { 74 TextPickerDialog.show({ 75 range: this.availableThings, // 将可选事项列表配置到选择对话框中 76 onAccept: (value: TextPickerResult) => { 77 this.toDoData.push(new ToDo(this.availableThings[value.index])); // 用户点击确认,将选择的数据添加到待办列表toDoData中 78 }, 79 }) 80 }) 81 ``` 82 83 84## 删除列表项 85 86如下图所示,当用户长按列表项进入删除模式时,提供用户删除列表项选择的交互界面,用户勾选完成后点击删除按钮,列表中删除对应的项目。 87 88 **图18** 长按删除待办事项 89 90 91 92### 开发步骤 93 941. 列表的删除功能一般进入编辑模式后才可使用,所以需要提供编辑模式的入口。 95 以待办列表为例,通过LongPressGesture()监听列表项的长按事件,当用户长按列表项时,进入编辑模式。 96 97 98 ```ts 99 // ToDoListItem.ets 100 101 Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { 102 ... 103 } 104 .gesture( 105 GestureGroup(GestureMode.Exclusive, 106 LongPressGesture() // 监听长按事件 107 .onAction(() => { 108 if (!this.isEditMode) { 109 this.isEditMode = true; //进入编辑模式 110 this.selectedItems.push(this.toDoItem); // 记录长按时选中的列表项 111 } 112 }) 113 ) 114 ) 115 ``` 116 1172. 需要响应用户的选择交互,记录要删除的列表项数据。 118 在待办列表中,通过勾选框的勾选或取消勾选,响应用户勾选列表项变化,记录所有选择的列表项。 119 120 ```ts 121 // ToDoListItem.ets 122 123 if (this.isEditMode) { 124 Checkbox() 125 .onChange((isSelected) => { 126 if (isSelected) { 127 this.selectedItems.push(this.toDoItem) // 勾选时,记录选中的列表项 128 } else { 129 let index = this.selectedItems.indexOf(this.toDoItem) 130 if (index !== -1) { 131 this.selectedItems.splice(index, 1) // 取消勾选时,则将此项从selectedItems中删除 132 } 133 } 134 }) 135 ... 136 } 137 ``` 138 1393. 需要响应用户点击删除按钮事件,删除列表中对应的选项。 140 141 ```ts 142 // ToDoList.ets 143 144 Button('删除') 145 .onClick(() => { 146 // 删除选中的列表项对应的toDoData数据 147 let leftData = this.toDoData.filter((item) => { 148 return this.selectedItems.find((selectedItem) => selectedItem !== item); 149 }) 150 151 this.toDoData = leftData; 152 this.isEditMode = false; 153 }) 154 ... 155 ``` 156## 完整示例代码 157新增和删除列表项的实现共涉及三个文件,各文件完整代码如下: 1581. 待办事项数据结构代码(ToDo.ets): 159 ```ts 160 // ToDo.ets 161 import util from '@ohos.util'; 162 163 export class ToDo { 164 key: string = util.generateRandomUUID(true) 165 name: string; 166 167 constructor(name: string) { 168 this.name = name; 169 } 170 } 171 ``` 1722. 待办事项列表代码(ToDoList.ets): 173 ```ts 174 // ToDoList.ets 175 import { ToDo } from '../model/ToDo'; 176 import { ToDoListItem } from './ToDoListItem'; 177 178 @Entry 179 @Component 180 struct ToDoList { 181 @State toDoData: ToDo[] = [] 182 @Watch('onEditModeChange') @State isEditMode: boolean = false 183 @State selectedItems: ToDo[] = [] 184 185 private availableThings: string[] = ["读书", "运动", "旅游", '听音乐', '看电影', '唱歌'] 186 187 saveData(value: string) { 188 this.toDoData.push(new ToDo(value)) 189 } 190 191 onEditModeChange() { 192 if (!this.isEditMode) { 193 this.selectedItems = [] 194 } 195 } 196 197 build() { 198 Column() { 199 Row() { 200 if (this.isEditMode) { 201 Text('X') 202 .fontSize(20) 203 .onClick(() => { 204 this.isEditMode = false; 205 }) 206 .margin({ left: 20, right: 20 }) 207 208 Text('已选择' + this.selectedItems.length + '项') 209 .fontSize(24) 210 } else { 211 Text('待办') 212 .fontSize(36) 213 .margin({ left: 40}) 214 Blank() 215 Text('+') 216 .fontWeight(FontWeight.Lighter) 217 .fontSize(40) 218 .margin({ right: 30 }) 219 .onClick(() => { 220 TextPickerDialog.show({ 221 range: this.availableThings, 222 onAccept: (value: TextPickerResult) => { 223 this.toDoData.push(new ToDo(this.availableThings[value.index])) 224 console.info('to do data: ' + JSON.stringify(this.toDoData)) 225 }, 226 }) 227 }) 228 } 229 } 230 .height('12%') 231 .width('100%') 232 233 List({ initialIndex: 0, space: 10 }) { 234 ForEach(this.toDoData, toDoItem => { 235 ListItem() { 236 ToDoListItem({ 237 isEditMode: $isEditMode, 238 toDoItem: toDoItem, 239 selectedItems: $selectedItems 240 }) 241 }.padding({ left: 24, right: 24, bottom: 12 }) 242 }, toDoItem => toDoItem.key) 243 } 244 .height('73%') 245 .listDirection(Axis.Vertical) 246 .edgeEffect(EdgeEffect.Spring) 247 248 if (this.isEditMode) { 249 Row() { 250 Button('删除') 251 .width('80%') 252 .onClick(() => { 253 let leftData = this.toDoData.filter((item) => { 254 return this.selectedItems.find((selectedItem) => selectedItem != item) 255 }) 256 console.log('leftData: ' + leftData); 257 this.isEditMode = false; 258 this.toDoData = leftData; 259 }) 260 .backgroundColor('#ffd75d5d') 261 } 262 .height('15%') 263 } 264 } 265 .backgroundColor('#fff1f3f5') 266 .width('100%') 267 .height('100%') 268 } 269 } 270 ``` 2713. 待办事项代码(ToDoListItem.ets): 272 ```ts 273 // ToDoListItem.ets 274 import { ToDo } from '../model/ToDo'; 275 276 @Component 277 export struct ToDoListItem { 278 @Link isEditMode: boolean 279 @Link selectedItems: ToDo[] 280 private toDoItem: ToDo; 281 282 hasBeenSelected(): boolean { 283 return this.selectedItems.indexOf(this.toDoItem) != -1 284 } 285 286 build() { 287 Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { 288 Row({ space: 4 }) { 289 Circle() 290 .width(24) 291 .height(24) 292 .fill(Color.White) 293 .borderWidth(3) 294 .borderRadius(30) 295 .borderColor('#ffdcdfdf') 296 .margin({ right: 10 }) 297 298 Text(`${this.toDoItem.name}`) 299 .maxLines(1) 300 .fontSize(24) 301 .textOverflow({ overflow: TextOverflow.Ellipsis }) 302 } 303 .padding({ left: 12 }) 304 305 if (this.isEditMode) { 306 Checkbox() 307 .select(this.hasBeenSelected() ? true : false) 308 .onChange((isSelected) => { 309 if (isSelected) { 310 this.selectedItems.push(this.toDoItem) 311 } else { 312 let index = this.selectedItems.indexOf(this.toDoItem) 313 if (index != -1) { 314 this.selectedItems.splice(index, 1) 315 } 316 } 317 }) 318 .width(24) 319 .height(24) 320 } 321 } 322 .width('100%') 323 .height(80) 324 .padding({ 325 left: 16, 326 right: 12, 327 top: 4, 328 bottom: 4 329 }) 330 .borderRadius(24) 331 .linearGradient({ 332 direction: GradientDirection.Right, 333 colors: this.hasBeenSelected() ? [[0xffcdae, 0.0], [0xFfece2, 1.0]] : [[0xffffff, 0.0], [0xffffff, 1.0]] 334 }) 335 .gesture( 336 GestureGroup(GestureMode.Exclusive, 337 LongPressGesture() 338 .onAction(() => { 339 if (!this.isEditMode) { 340 this.isEditMode = true 341 this.selectedItems.push(this.toDoItem) 342 } 343 }) 344 ) 345 ) 346 } 347 } 348 ```