常规方法: 在Javabean里增加一个boolean isSelected字段, 并在Adapter里根据这个字段的值设置“CheckBox”的选中状态。 在每次选中一个新优惠券时,改变数据源里的isSelected字段, 并notifyDataSetChanged()刷新整个列表。 这样实现起来很简单,代码量也很少,唯一不足的地方就是性能有损耗,不是最优雅。 So作为一个有追求 今天比较闲 的程序员,我决心分享一波优雅方案。
本文会列举分析一下在ListView和RecyclerView中, 列表实现单选的几种方案,并推荐采用定向刷新 部分绑定的方案,因为更高效and优雅
1常规方案:
常规方案 请光速阅读,直接上码: Bean结构:
1 | public class TestBean extends SelectedBean { |
我项目里有好多单选需求,懒得写isSelected字段,所以弄了个父类供子类继承。
1 | public class SelectedBean { |
Acitivity 和Adapter其他方法都是最普通的不再赘述。 Adapter的onBindViewHolder()如下:
1 | Log.d("TAG", "onBindViewHolder() called with: holder = [" + holder + "], position = [" + position + "]"); |
- 方案优点:简单粗暴
- 方案缺点: 其实需要修改的Item只有两项: 一个当前处于选中状态的Item->普通状态 再将当前手指点击的这个Item->选中状态 但采用普通方案,则会刷新整个一屏可见的Item,重走他们的getView()/onBindViewHolder()方法。 其实一个屏幕一般最多可见10+个Item,遍历一遍也无伤大雅。 但咱们还是要有追求优雅的心,所以我们继续往下看。
2 利用Rv的notifyItemChanged()定向刷新:
本方案可以中速阅读
本方案需要在Adapter里新增一个字段:
1
private int mSelectedPos = -1;//实现单选 方法二,变量保存当前选中的position
在设置数据集时(构造函数,setData()方法等:),初始化 mSelectedPos 的值。
1
2
3
4
5
6//实现单选方法二: 设置数据集时,找到默认选中的pos
for (int i = 0; i < mDatas.size(); i++) {
if (mDatas.get(i).isSelected()) {
mSelectedPos = i;
}
}onClick里代码如下:
1
2
3
4
5
6
7
8
9
10
11//实现单选方法二: notifyItemChanged() 定向刷新两个视图
//如果勾选的不是已经勾选状态的Item
if (mSelectedPos!=position){
//先取消上个item的勾选状态
mDatas.get(mSelectedPos).setSelected(false);
notifyItemChanged(mSelectedPos);
//设置新Item的勾选状态
mSelectedPos = position;
mDatas.get(mSelectedPos).setSelected(true);
notifyItemChanged(mSelectedPos);
}
本方案由于调用了notifyItemChanged(),所以还会伴有“白光一闪”的动画。
方案优点: 本方案,较优雅了,不会重走一屏可见的Item的getView()/onBindViewHolder()方法, 但仍然会重走需要修改的两个Item的getView()/onBindViewHolder()方法,
方案缺点: 我们实际上需要修改的,只是里面“CheckBox”的值, 按照在DiffUtil一文学习到的姿势,术语应该是“Partial bind “, (安利时间,没听过DiffUtil和Partial bind的 戳->:【Android】详解7.0带来的新工具类:DiffUtil) 我们需要的只是部分绑定。
一个疑点: 使用方法2 在第一次选中其他Item时,切换selected状态时, 查看log,并不是只重走了新旧Item的onBindViewHolder()方法,还走了两个根本不在屏幕范围里的Item的onBindViewHolder()方法, 如,本例中 在还有item 0-3 在屏幕里,默认勾选item1,我选中item0后,log显示postion 4,5,0,1 依次执行了onBindViewHolder()方法。 但是再次切换其他Item时, 会符合预期:只走需要修改的两个Item的getView()/onBindViewHolder()方法。 原因未知,有朋友知道烦请告知,多谢。
3 Rv 实现部分绑定(推荐):
利用RecyclerView的 findViewHolderForLayoutPosition()方法,获取某个postion的ViewHolder,按照源码里这个方法的注释,它可能返回null。所以我们需要注意判空,(空即在屏幕不可见)。 与方法2只有onClick里的代码不一样,核心还是利用mSelectedPos 字段搞事情。
1 | //实现单选方法三: RecyclerView另一种定向刷新方法:不会有白光一闪动画 也不会重复onBindVIewHolder |
- 方案优点: 定向刷新两个Item,只修改必要的部分,不会重走onBindViewHolder(),属于手动部分绑定。代码量也适中,不多。
- 方案缺点: 没有白光一闪动画???(如果这算缺点)
4 Rv 利用payloads实现部分绑定(不推荐):
本方案属于开拓思维,是在方案2的基础上,利用payloads和notifyItemChanged(int position, Object payload)搞事情。 不知道payloads是什么的,看不懂此方案的,我又要安利:(戳->:【Android】详解7.0带来的新工具类:DiffUtil) onClick代码如下:
1 | //实现单选方法四: |
需要重写三参数的onBindViewHolder() 方法:
1 | @Override |
方案优点: 同方法3
方案缺点: 代码量多,实现效果和方法三一样,仅做开拓思维用,所以选择方法三。
作者:张旭童 链接:http://www.jianshu.com/p/1ac13f74da63 來源:简书