【Android】 RecyclerView、ListView实现单选列表的优雅之路

Log.d(“TAG”, “onBindViewHolder() called with: holder = [” + holder + “], position = [” + position + “]”);

holder.ivSelect.setSelected(mDatas.get(position).isSelected());//“CheckBox”

holder.tvCoupon.setText(mDatas.get(position).getName());//TextView

holder.ivSelect.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

//实现单选,第一种方法,十分简单, Lv Rv通用,因为它们都有notifyDataSetChanged()方法

// 每次点击时,先将所有的selected设为false,并且将当前点击的item 设为true, 刷新整个视图

for (TestBean data : mDatas) {

data.setSelected(false);

}

mDatas.get(position).setSelected(true);

notifyDataSetChanged();

}

});

ViewHolder:

public static class CouponVH extends RecyclerView.ViewHolder {

private ImageView ivSelect;

private TextView tvCoupon;

public CouponVH(View itemView) {

super(itemView);

ivSelect = (ImageView) itemView.findViewById(R.id.ivSelect);

tvCoupon = (TextView) itemView.findViewById(R.id.tvCoupon);

}

}

方案优点:

简单粗暴

方案缺点:

其实需要修改的Item只有两项

一个当前处于选中状态的Item->普通状态

再将当前手指点击的这个Item->选中状态

但采用普通方案,则会刷新整个一屏可见的Item,重走他们的getView()/onBindViewHolder()方法。

其实一个屏幕一般最多可见10+个Item,遍历一遍也无伤大雅。

但咱们还是要有追求优雅的心,所以我们继续往下看。

2 利用Rv的notifyItemChanged()定向刷新:

本方案可以中速阅读

⑴本方案需要在Adapter里新增一个字段:

private int mSelectedPos = -1;//实现单选 方法二,变量保存当前选中的position

⑵在设置数据集时(构造函数,setData()方法等:),初始化 mSelectedPos 的值。

//实现单选方法二: 设置数据集时,找到默认选中的pos

for (int i = 0; i < mDatas.size(); i++) {

if (mDatas.get(i).isSelected()) {

mSelectedPos = i;

}

}

⑶onClick里代码如下:

//实现单选方法二: 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()方法,

但仍然会重走需要修改的两个ItemgetView()/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 字段搞事情。

//实现单选方法三: RecyclerView另一种定向刷新方法:不会有白光一闪动画 也不会重复onBindVIewHolder

CouponVH couponVH = (CouponVH) mRv.findViewHolderForLayoutPosition(mSelectedPos);

if (couponVH != null) {//还在屏幕里

couponVH.ivSelect.setSelected(false);

}else {

//add by 2016 11 22 for 一些极端情况,holder被缓存在Recycler的cacheView里,

//此时拿不到ViewHolder,但是也不会回调onBindViewHolder方法。所以add一个异常处理

notifyItemChanged(mSelectedPos);

}

mDatas.get(mSelectedPos).setSelected(false);//不管在不在屏幕里 都需要改变数据

//设置新Item的勾选状态

mSelectedPos = position;

mDatas.get(mSelectedPos).setSelected(true);

holder.ivSelect.setSelected(true);

方案优点:

定向刷新两个Item,只修改必要的部分,不会重走onBindViewHolder(),属于手动部分绑定。代码量也适中,不多。

方案缺点:

没有白光一闪动画???(如果这算缺点)

4 Rv 利用payloads实现部分绑定(不推荐):

本方案属于开拓思维,是在方案2的基础上,利用payloads和notifyItemChanged(int position, Object payload)搞事情。

不知道payloads是什么的,看不懂此方案的,我又要安利:(戳->:【Android】详解7.0带来的新工具类:DiffUtil

onClick代码如下:

//实现单选方法四:

if (mSelectedPos != position) {

//先取消上个item的勾选状态

mDatas.get(mSelectedPos).setSelected(false);

//传递一个payload

Bundle payloadOld = new Bundle();

payloadOld.putBoolean(“KEY_BOOLEAN”, false);

notifyItemChanged(mSelectedPos, payloadOld);

//设置新Item的勾选状态

mSelectedPos = position;

mDatas.get(mSelectedPos).setSelected(true);

Bundle payloadNew = new Bundle();

payloadNew.putBoolean(“KEY_BOOLEAN”, true);

notifyItemChanged(mSelectedPos, payloadNew);

}

需要重写三参数的onBindViewHolder() 方法:

@Override

public void onBindViewHolder(CouponVH holder, int position, List payloads) {

if (payloads.isEmpty()) {

onBindViewHolder(holder, position);

} else {

Bundle payload = (Bundle) payloads.get(0);

if (payload.containsKey(“KEY_BOOLEAN”)) {

boolean aBoolean = payload.getBoolean(“KEY_BOOLEAN”);

holder.ivSelect.setSelected(aBoolean);

}

}

}

方案优点:

同方法3

方案缺点:

代码量多,实现效果和方法三一样,仅做开拓思维用,所以选择方法三。


三 ListView 方案一览:


老实说,现在如果你还在用ListView,不是历史遗留问题的话,你需要面壁思过。

但是毕竟还有人在用,就像还有人在用Android4.x,咱也要考虑这部分人的感受是不是。

1 常规方案:

常规方案 和Rv一毛一样,不上码,参考 二.1:

方案优点:

同 二.1

方案缺点:

同 二.1

2 ListView里寻找优雅之路:

此方案,思路是同二.3。

只不过ListView没有提供 findViewHolderForLayoutPosition() 这种方法,通过postion获取缓存的ViewHolder。这是废话,因为它设计的时候就没有强迫我们使用ViewHolder模式,所以我们是获取不到ViewHolder的,那么我们另辟蹊径,直接通过ViewGroup的getChildAt() 获取子View,拿到子View就能拿到ViewHolder,就能搞事情。上码:

//实现单选:方法二:Lv的定向刷新

//如果 当前选中的View 在当前屏幕可见,且不是自己,要定向刷新一下之前的View的状态

if (position != mSelectedPos) {

int firstPos = mLv.getFirstVisiblePosition() - mLv.getHeaderViewsCount();//这里考虑了HeaderView的情况

int lastPos = mLv.getLastVisiblePosition() - mLv.getHeaderViewsCount();

if (mSelectedPos >= firstPos && mSelectedPos <= lastPos) {

View lastSelectedView = mLv.getChildAt(mSelectedPos - firstPos);//取出选中的View

CouponVH lastVh = (CouponVH) lastSelectedView.getTag();

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
[外链图片转存中…(img-n9cyMTKW-1715797003134)]

[外链图片转存中…(img-oL7UqNeI-1715797003136)]

[外链图片转存中…(img-uEIFa4EP-1715797003137)]

[外链图片转存中…(img-ppnuQdjf-1715797003138)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 14
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值