多选功能小姐
文章目录
其实不是小姐,是小节。
这周接到一个需求,完成一个多选删除以及多选转发的功能。删除以及转发的功能已经有了,注意是要实现多选的操作。
先说多选
这里的界面是ListView
+ CursorAdapter
实现的。我需要在 ListView
的 ItemView
里面加一个CheckBox
.
不得不提一下,这里的布局写的非常的混乱。里面包含多种类型的Item
的,理论上,使用不同的ItemType
去加载不同的ItemView
是非常合适的。但是原来的界面不是这样玩的。是直接在一个ItemView
里面通过逻辑去判断显示或者隐藏那部分View
. 行吧,这样也是可以的,只是布局就比较混乱了。然后更糟糕的是,这里的布局还分左右两种情况,然后布局是同一个布局,然后在代码里面动态改变。具体怎么改的呢?
if (pos == RIGHT) {
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); // 添加该规则
lp.addRule(RelativeLayout.RIGHT_OF, 0); // 屏蔽该规则
} else {
lp.addRule(RelativeLayout.RIGHT_OF, R.id.item_check);
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0);
}
这段代码涉及到 RelativeLayout.LayoutParams.addRule()
这个方法。这个方法很有意思。尤其是带两个参数的。如果第二个参数是0
,说明这个规则不起作用。这样做有什么意义呢?意义就在于,如果之前添加了这个规则,通过这样的方式,可以让之前添加的该规则失效。
先把Checkbox
放置到正确的位置。
好了,既然是这样的方式。我的CheckBox
又是需要加在每个ItemView
的最左边的。那么我就也按照addRule
的方式,把我的Checkbox
放到对应的位置上面去了。
然后要根据不同的Item
类型,设置Checkbox
的状态。
分别是不可用,未选中,选中 这3种状态。
这个要怎么弄呢?我先是根据对应的POJO
对象,在里面添加一个属性int checkState;
去处理的。但是,一直不行,滑动之后,选中状态全部混乱了。我猜想不可行的原因可能是,这里的POJO
对象是根据Cursor
生成的。而根据Cursor
生成,又会将之前设置的checkState
恢复为默认值。(这个只是猜想,没有去验证。)
既然此路不通,我就变通一下。我不再去设置对应的POJO
对象的checkState
属性了。我专门搞了一个成员变量HashMap<Integer,Integer>
.其中的映射关系是:{key:position,value:checkState}
;这样就靠谱了。你不是根据Cursor
去生成对应的POJO
对象的吗,行。我的position
也是通过Cursor
拿到的。int position = cursor.getPosition();
然后可不可用,是具体原有的逻辑来的。比如:int checkState = item.checkable? 0:1
; 这样,可用不可用状态就搞定了。但是,用户点击选中的状态还是没有保存。
那么就通过设置Checkbox
的点击事件去做这件事。
cb.setOnclickListener(v->{
// 代码中将 0,1,2 定义成常量了,这里是为了验证效果
// 0 - disable
// 1 - unChekced
// 2 - checked
checkMap.put(pos, checkMap.get(pos)==1? 2:1);
cb.setChecked(checkMap.get(pos) == 2);
});
好了, 通过设置点击事件,也将CheckBox
的选中不选中状态保存了。
到此为止,checkbox
的3种状态都搞定了,列表怎么滑动都不会出现选中状态错乱的问题了。
至于样式,这个直接在布局里面设置对应的
style
去控制的。但是实际上滚动之后,样式不对了,我又在代码里面cb.setXXX();
之后调用了cb.setButtonDrawable(R.drawable.cb_multi_selector);
那么,现在就要把之前多选对应的数据给获取到,一遍后续的删除,转发操作的执行。
那么,之前保存的,只是{position:checkState}
,现在要拿到数据,那很简单。根据对应的position
以及 checkState == 2
然后把对应的POJO
对象存起来即可。
做法就是弄一个成员变量private List<Bean> selectedList = new ArrayList<>;
, 然后在CheckBox
的点击事件里面做对应的list.add(bean); / list.remove(Bean);
的操作即可。
cb.setOnClickListener(v->{
// ... 设置选中状态的逻辑先
if( checkMap.get(position) == 2 ) {
selectedList.add(bean);
} else {
selectedList.remove(bean);
}
});
OK , 这样一来,就把选中的数据给保存了。
然后在执行删除的时候,就去执行删除操作:ItemUtils.delete(seletedList);
执行转发的时候,也是去执行转发:ItemUtils.forward(selectedList);
多说一句,在执行多选删除的时候,这里是执行了数据操作。涉及到一个批处理操作。原有的逻辑是删除单条数据的,没有批处理。而这里就需要使用批处理了。不然每次删除一个都会触发删除成功的回调,非常烦人。
这里用到了ContenResolver.applyBatch();
的操作。
public ContentProviderResult[] applyBatch(@NonNull String authority,
@NonNull ArrayList<ContentProviderOperation> operations){}
authority
: 这个不用说的, contenProvider 必然会配置这个
operations
这个就是待处理的数据集合
ContentProviderOperation
的操作还是比较简单的,没有用过也能立即明白。
这里既然是删除,那就是 ContentProviderOperation.newDelete(uri).build();
里面还有 withXXX()
这样的辅助方法。(我觉得withXXX
是给newUpdate()
用的。)
这些参数构造完成了,调用一下 ContenResolver.applyBatch(authorty,ops);
即可。
至于转发,这里完全是一个原有逻辑,不说了。
这个小姐大致就是这样子了。错了,是小节。