1# 如何删除多选框选项
2## 场景说明
3通常情况下,我们使用多选框都会伴随对选项的操作,比较常见的操作是选中后删除,比如删除购物车的商品、删除账单、删除图片等等。但是,当前OpenHarmony针对多选框组件并没有提供直接的删除其选项的方法,需要开发者自己来实现。本例提供了一种实现方案,供开发者参考。
4
5## 效果呈现
6本例最终效果如下:
7
8![delete-checkboxitem](figures/delete-checkboxitem.gif)
9
10
11## 运行环境
12本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:
13
14- IDE: DevEco Studio 4.0 Beta1
15- SDK: Ohos_sdk_public 4.0.7.5 (API Version 10 Beta1)
16
17
18## 实现思路
19本例中涉及的关键特性及实现方案如下:
20- 多选框:使用CheckboxGroup组件构建多选框标题,使用Checkbox组件构建多选框选项。
21- 删除多选框选项:通过CheckboxGroup的onChange回调获取到各个选项的选中状态,在删除操作中,将选中的选项从选项列表中删除。
22- 删除时弹出确认框:使用promptAction模块调用showDialog方法弹出对话框,通过回调获取到用户点击的是取消按钮还是确定按钮,如果是确定按钮则执行删除操作。
23
24## 开发步骤
251. 搭建UI布局。
26    整体纵向布局,那就采用Column组件;全选框用CheckboxGroup组件,然后每个选项都包括一个选择框(Checkbox组件)和一个文本(Text组件),且为横向布局,那我们可以把它们放在Flex组件中;最后是一个Button组件。这样整体布局就有了,具体代码如下:
27    ```ts
28    @Component
29    struct CheckboxDemo{
30      build(){
31        Column(){
32          Flex({}){
33            CheckboxGroup({})
34            Text('水果清单')
35          }
36          Flex({}){
37            Checkbox({})
38            Text('苹果')
39          }
40          Flex({}){
41            Checkbox({})
42            Text('菠萝')
43          }
44          Flex({}){
45            Checkbox({})
46            Text('柚子')
47          }
48          Button('删除')
49        }
50      }
51    }
52    ```
53    框架搭好了,看下效果:
54
55    ![checkbox-before-improve](figures/checkbox-before-improve.PNG)
56
57    可以看到主选框和选项对齐了,接下来我们来调整下样式。
58    先给CheckboxGroup取个名字:fruit_list,然后为各个Checkbox添加相同的group名称,这样就可以将Checkbox挂靠到CheckboxGroup下,剩下的就是给各个组件添加margin、fontSize等通用属性了,不清楚各个组件有哪些属性的请自行查阅组件参考,具体代码如下:
59    ```ts
60    @Component
61    struct CheckboxDemo{
62      build(){
63        Column(){
64          Flex({justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
65            // 将CheckboxGroup命名为'fruit_list'
66            CheckboxGroup({group: 'fruit_list'})
67              .selectedColor('#007DFF')
68            Text('水果清单')
69              .margin({right:20})
70              .fontSize(14)
71              .lineHeight(20)
72              .fontColor('#182431')
73              .fontWeight(FontWeight.Bold)
74          }
75          Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
76            // 通过group参数将Checkbox挂到CheckboxGroup下
77            Checkbox({name:'苹果',group:'fruit_list'})
78              .selectedColor('#007DFF')
79            Text('苹果')
80              .margin({right:20})
81              .fontSize(14)
82              .lineHeight(20)
83              .fontColor('#182431')
84              .fontWeight(500)
85          }
86          Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
87            Checkbox({name:'菠萝',group:'fruit_list'})
88              .selectedColor('#007DFF')
89            Text('菠萝')
90              .margin({right:20})
91              .fontSize(14)
92              .lineHeight(20)
93              .fontColor('#182431')
94              .fontWeight(500)
95          }
96          Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
97            Checkbox({name:'柚子',group:'fruit_list'})
98              .selectedColor('#007DFF')
99            Text('柚子')
100              .margin({right:20})
101              .fontSize(14)
102              .lineHeight(20)
103              .fontColor('#182431')
104              .fontWeight(500)
105          }
106          Button('删除')
107            .margin({top:20,left:35})
108            .fontSize(15)
109            .padding({top:5,bottom:5,left:15,right:15})
110        }
111      }
112    }
113    ```
114    我们再来看下效果,发现多选框的布局已经正常了:
115
116    ![checkbox-after-improve](figures/checkbox-after-improve.PNG)
1172. 简化代码。
118    当前代码重复性很高,包含Checkbox的三个Flex的结构完全一样,我们可以简化一下。把重复的结构通过@Builder抽取出来作为一个自定义组件,用到的地方进行引用即可;每个Flex呈现的内容不同,那就将不同的内容作为参数传入。具体代码如下:
119    ```ts
120    @Component
121    struct CheckboxDemo{
122      // flexNameList存储checkbox选项的文本内容;使用@State修饰后,flexNameList发生变化,UI会同步刷新
123      @State flexNameList:string[] = ['苹果','菠萝','柚子']
124      @Builder
125      // 将重复内容封装成flexItem,通过参数checkboxName为Text组件传入显示文本,通过groupName绑定CheckboxGroup
126      flexItem(checkboxName:string,groupName:string){
127        Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
128          Checkbox({ name:checkboxName, group:groupName})
129            .selectedColor('#007DFF')
130          Text(checkboxName)
131            .margin({right:20})
132            .fontSize(14)
133            .lineHeight(20)
134            .fontColor('#182431')
135            .fontWeight(500)
136        }.margin({ left: 36 })
137      }
138
139      build(){
140        Column(){
141          Flex({justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
142            CheckboxGroup({group: 'fruit_list'})
143              .selectedColor('#007DFF')
144            Text('水果清单')
145              .margin({right:20})
146              .fontSize(14)
147              .lineHeight(20)
148              .fontColor('#182431')
149              .fontWeight(FontWeight.Bold)
150          }
151          // 通过ForEach遍历flexNameList循环渲染生成UI
152          ForEach(this.flexNameList,(item:any)=>{
153            // 引用封装的flexItem模板
154            this.flexItem(item,'fruit_list')
155          })
156
157          Button('删除')
158            .margin({top:20,left:35})
159            .fontSize(15)
160            .padding({top:5,bottom:5,left:15,right:15})
161        }.alignItems(HorizontalAlign.Center)
162      }
163    }
164    ```
1653. 添加删除逻辑。
166    本例中是通过以下方式实现删除操作的:将选中的水果项从flexNameList中删除(为方便展示,本文直接将数据存储在数组中,实际开发需要对接数据库),由于flexNameList被@State修饰,所以其发生变化时会重新执行Build(),从而完成UI刷新,展示删除后的选项。
167    要实现上述逻辑,首先需要获取到被选中的水果项。这里可以通过CheckboxGroup的onChange回调获取,当选项的状态发生变化时,会触发回调并返回各选项的选中状态。
168    具体代码如下:
169    ```ts
170    @Component
171    struct CheckboxDemo{
172     ...
173      // itemStatus用来存储各选项的选中状态
174      private itemStatus: CheckboxGroupResult
175      ...
176      build(){
177        Column(){
178          Flex({justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
179            CheckboxGroup({group: 'fruit_list'})
180              .selectedColor('#007DFF')
181              // 选项状态发生变化时触发onChange回调,各选项的选中状态储存在itemName中并返回,通过itemStatus.name可以获取到被选中的选项列表
182              .onChange((itemName: CheckboxGroupResult) => {
183                this.itemStatus = itemName
184              })
185            ...
186        }.alignItems(HorizontalAlign.Center)
187      }
188    }
189    ```
190    将各选项的选中状态存储到itemStatus后,我们就可以通过Button触发删除操作了。当点击删除按钮时,触发删除操作,所以给Button添加onClick事件,并添加删除逻辑。代码如下:
191    ```ts
192    @Component
193    struct CheckboxDemo{
194     ......
195      // itemStatus用来存储各选项的选中状态
196      private itemStatus: CheckboxGroupResult
197      ......
198      build(){
199        Column(){
200          Flex({justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
201            CheckboxGroup({group: 'fruit_list'})
202              .selectedColor('#007DFF')
203              // 选项状态发生变化时触发onChange回调,各选项的选中状态储存在itemName中并返回,通过itemStatus.name可以获取到被选中的选项列表
204              .onChange((itemName: CheckboxGroupResult) => {
205                this.itemStatus = itemName
206              })
207            ......
208          Button('删除')
209            .margin({top:20,left:35})
210            .fontSize(15)
211            .padding({top:5,bottom:5,left:15,right:15})
212            // 点击触发删除操作
213            .onClick(()=>{
214            // 被选中的项存储在this.itemStatus.name列表中
215              for(let i of this.itemStatus.name){
216                // 从flexNameList中删除被选中的项,刷新UI
217                this.flexNameList.splice(this.flexNameList.indexOf(i),1)
218              }
219            })
220        }.alignItems(HorizontalAlign.Center)
221      }
222    }
223    ```
2244. 添加删除确认框。
225    使用promptAction模块调用showDialog方法弹出对话框,然后将删除操作绑定到对话框的确定按钮,具体代码如下:
226    ```ts
227    // 导入promptAction模块
228    import promptAction from '@ohos.promptAction';
229    @Component
230    struct CheckboxDemo{
231     ......
232      // itemStatus用来存储各选项的选中状态
233      private itemStatus: CheckboxGroupResult
234      ......
235      build(){
236        Column(){
237          Flex({justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
238            CheckboxGroup({group: 'fruit_list'})
239              .selectedColor('#007DFF')
240              // 选项状态发生变化时触发onChange回调,各选项的选中状态储存在itemName中并返回,通过itemStatus.name可以获取到被选中的选项列表
241              .onChange((itemName: CheckboxGroupResult) => {
242                this.itemStatus = itemName
243              })
244            ......
245          Button('删除')
246            .margin({top:20,left:35})
247            .fontSize(15)
248            .padding({top:5,bottom:5,left:15,right:15})
249            // 点击触发删除操作
250            .onClick(()=>{
251              // 调用对话框
252              promptAction.showDialog({
253                title:'',
254                message:'确定删除吗?',
255                buttons:[
256                {text:'取消', color:'#000000'},
257                {text:'确定', color:'#000000'}
258                ]
259              })
260                // 用户选择通过data回传,当data.index为1时,用户选择确定,当data.index为0时,用户选择取消
261                .then(data=>{
262                  // 当用户选择确定时,进行删除操作
263                  if(data.index===1){
264                    // 被选中的项存储在this.itemStatus.name列表中
265                    for(let i of this.itemStatus.name){
266                      // 从flexNameList中删除被选中的项,刷新UI
267                      this.flexNameList.splice(this.flexNameList.indexOf(i),1)
268                    }
269                  }
270                })
271            })
272        }.alignItems(HorizontalAlign.Center)
273      }
274    }
275    ```
276## 完整代码
277本例完整代码如下:
278```ts
279import promptAction from '@ohos.promptAction';
280
281@Entry
282@Component
283struct CheckboxDemo{
284  @State flexNameList:string[] = ['苹果','菠萝','柚子']
285  private itemStatus:CheckboxGroupResult
286  @Builder
287  flexItem(checkboxName:string,groupName:string){
288    Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
289      Checkbox({ name:checkboxName, group:groupName})
290        .selectedColor('#007DFF')
291      Text(checkboxName)
292        .margin({right:20})
293        .fontSize(14)
294        .lineHeight(20)
295        .fontColor('#182431')
296        .fontWeight(500)
297    }.margin({ left: 36 })
298  }
299
300  build() {
301    Column() {
302      if (this.flexNameList.length != 0){
303        Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
304          CheckboxGroup({ group: 'fruit_list' })
305            .selectedColor('#007DFF')
306            .onChange((itemName: CheckboxGroupResult) => {
307              this.itemStatus = itemName
308            })
309          Text('水果清单')
310            .margin({right:20})
311            .fontSize(14)
312            .lineHeight(20)
313            .fontColor('#182431')
314            .fontWeight(FontWeight.Bold)
315        }.margin({top:150})
316
317        ForEach(this.flexNameList,(item:any)=>{
318          this.flexItem(item,'fruit_list')
319        })
320
321        Button("删除")
322          .margin({top:20,left:35})
323          .fontSize(15)
324          .padding({top:5,bottom:5,left:15,right:15})
325          .onClick(()=>{
326            promptAction.showDialog({
327              title:'',
328              message:'确定删除吗?',
329              buttons:[
330                {text:'取消', color:'#000000'},
331                {text:'确定', color:'#000000'}
332              ]
333            })
334              .then(data=>{
335                if(data.index===1){
336                  for(let i of this.itemStatus.name){
337                    this.flexNameList.splice(this.flexNameList.indexOf(i),1)
338                  }
339                }
340              })
341          })
342      }else{
343
344      }
345    }.alignItems(HorizontalAlign.Center)
346  }
347}
348```
349## 参考
350
351- [ChecckboxGroup](../application-dev/reference/apis-arkui/arkui-ts/ts-basic-components-checkboxgroup.md)
352- [Checkbox](../application-dev/reference/apis-arkui/arkui-ts/ts-basic-components-checkbox.md)
353- [Flex](../application-dev/reference/apis-arkui/arkui-ts/ts-container-flex.md)
354- [Button](../application-dev/reference/apis-arkui/arkui-ts/ts-basic-components-button.md)
355- [ohos.promptAction (弹窗)](../application-dev/reference/apis-as/js-apis-promptAction.md)
356- [ForEach循环渲染](../application-dev/quick-start/arkts-rendering-control-foreach.md)
357- [@State状态管理](../application-dev/quick-start/arkts-state.md)
358- [@Builder动态构建UI元素](../application-dev/quick-start/arkts-builder.md)