内容简介:项目中经常性会碰到列表的单选、多选,实现起来好像也不难,但是最近项目有好多个需要单选/多选的页面,看到设计稿的一瞬间,脑子灵光一闪,为啥不把这些简单而又繁琐的逻辑给封装起来呢(懒癌发作)?于是就有了下面的小东西(开源库)...废话少说,先上图:
项目中经常性会碰到列表的单选、多选,实现起来好像也不难,但是最近项目有好多个需要单选/多选的页面,看到设计稿的一瞬间,脑子灵光一闪,为啥不把这些简单而又繁琐的逻辑给封装起来呢(懒癌发作)?
于是就有了下面的小东西(开源库)...
二 功能
1.列表单选
- 普通单选
- 预选中
- 不能取消
- 多类型
- 多列表
2.列表多选
- 普通多选
- 预选中
- 全选/取消全选
- 多类型
- 多列表
3.优点
Adapter.notifyItemChange
三 效果
废话少说,先上图:
1.单选
2.多选
四 使用
1.配置
首先在你的工程根目录的 gradle 文件下添加以下配置:
allprojects { repositories { ... maven { url 'https://jitpack.io' } } } 复制代码
接着就可以在你的moudle,一般是 app 的 gradle 文件下添加依赖:
implementation 'com.github.gminibird:CheckHelper:1.0' 复制代码
最后那个1.0是版本号,可以上 GitHub 上看最新的,然后就可以愉快的玩耍啦。
2.使用
- 创建CheckHelper实例
SingleCheckHelper mCheckHelper = new SingleCheckHelper(); //or MultiCheckHelper mCheckHelper = new MultiCheckHelper(); 复制代码
- 注册选择器
mCheckHelper.register(String.class, new CheckHelper.Checker<String, LwViewHolder>() @Override public void check(String s, LwViewHolder holder) { //选中状态 holder.itemView.setBackgroundColor(0xFF73E0E4); //蓝色 holder.setChecked(R.id.checkbox, true); } @Override public void unCheck(String s, LwViewHolder holder) { //非选中状态 holder.itemView.setBackgroundColor(0xFFFFFFFF); //白色 holder.setChecked(R.id.checkbox, false); } }); 复制代码
- 绑定到Adapter中
@Override protected void onBind(@NonNull LwViewHolder holder, @NonNull String item) { //这里用了自己封装的Adapter,相当于onBindViewHolder方法 mCheckHelper.bind(item, holder, holder.itemView); } 复制代码
然后,然后就完成了。。。运行就可以看到想要的效果,选中的数据可以调用相应 CheckHelper
实例的 getXXX()
获取。
五 实现原理
总的原理其实很简单,就是对应的 CheckHelper 实现类内部维护一些选中的数据。下面细说下具体实现:
1. 模板类 CheckHelper
CheckHelper
是目前两个实现类的基类,提供了一些基础的公共功能,比如设置监听器,注册选择器等,其实就是一个模板模式,把公共部分以及执行顺序都定了,然后交由子类完成具体的数据增改。
里面有两个重要的方法,一个是 bind()
,另一个是 select()
,分别对应了 onBindViewHolder
以及 onClick
(点击)方法:
public final void bind(final Object d, final RecyclerView.ViewHolder v, View clickedView) { if (clickedView == null) { throw new NullPointerException("ClickedView can not be null!"); } bind(d, v, isChecked(d, v)); //注释1 clickedView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { select(d, v); //注释2 } }); } 复制代码
我们在 onBindViewHolder()
方法中绑定的 bind()
方法最终会调用这个方法,先看 注释1 , 注释1 里的bind方法里面做了一系列的监听器的回调:
public void bind(Object d, RecyclerView.ViewHolder v, boolean toCheck) { Checker checker = mCheckerMap.get(d.getClass()); if (checker != null) { if (toCheck) { checker.check(d, v); } else { checker.unCheck(d, v); } } OnCheckListener checkListener = mCheckListenerMap.get(d.getClass()); if (checkListener != null) { checkListener.onCheck(d, v, toCheck); } onBindListener bindListener = mOnBindListenerMap.get(d.getClass()); if (bindListener != null) { bindListener.onBind(d, v, toCheck); } } 复制代码
其中包括 Checker
,也就是我们注册为非选中和选中状态设置的选择器会被调用,然后就是 OnCheckListener
以及 onBindListener
,这两个我们都可以add 进去。
再返回看最上面的bind() 方法里面的 注释2 ,调用了一系列回调后,然后便为 item 的某个 view 设置一个监听,点击后就会执行 select
方法, select
方法里面也是做了一系列回调,基本和上面的方法一致。所以子类只要重写这两个方法,并实现相应的数据操作即可。
2. SingleCheckHelper
单选
单选其实不难,但有一点比较难搞:
选中后怎么把上一次选中给取消?
如果按照平时的写法,我们在Bean里面增加一个 isChecked
字段,然后选择这个时将当前选中置为 true
,接着将上一个选择的置为 false
最后调用 adapter 刷新一下就可以了。
但是这里没有依赖具体的Bean,也没有新增字段,那怎么弄呢,后来想到了一个小技巧,我没有依赖Bean,但是可以在 ViewHolder 里面存储信息,所以就有下面的代码:
@Override public void select(Object d, RecyclerView.ViewHolder v, boolean toCheck) { if (toCheck) { unCheckPre(d); //注释1 setTag(v); //注释2 this.v = v; this.d = d; } else { if (!canCancel){ return; } clearTag(v); //注释3 this.d = null; this.v = null; } super.select(d, v, toCheck); } 复制代码
逻辑也很简单,如果是选中状态,而且存有tag,那么说明是之前已经有选中的,就将之前选中的tag清除,然后再把当前的选中设置一个tag,如果是取消选中,那么就将tag给清除掉:
private void unCheckPre(Object d) { if (this.d == null || this.d.equals(d)) { return; } if (this.d != null && this.v != null && this.v.itemView.getTag(TAG) != null) { //当上一个选中存在并且可见时置为非选 bind(d, v, false); } } private void setTag(RecyclerView.ViewHolder v) { if (v != null) { v.itemView.setTag(TAG, TAG_VALUE); } } 复制代码
接着就回调父类的一系列回调接口了。
3.MultiCheckHelper 多选
多选类主要是靠一个 Map 来维护选中的数据,每个数据类型对应一个 Set ,选中和非选中就调用相应的方法更新数据,没有很大难点。
protected HashMap<Class, Set<?>> mMap; @SuppressWarnings("unchecked") public void add(Object d) { Set<Object> set = (Set<Object>) mMap.get(d.getClass()); if (set == null) { set = new HashSet<>(); mMap.put(d.getClass(), set); } set.add(d); } public void remove(Object d) { Set set = mMap.get(d.getClass()); if (set != null) { set.remove(d); if (set.size() == 0) { mMap.remove(d.getClass()); } } } 复制代码
由于作者水平有限,如果有更好的方法欢迎探讨。
written by gminibird
源码戳上面 ^^^
以上所述就是小编给大家介绍的《你还在为列表[单选]、[多选]写重复的逻辑吗》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 以列表页为例,谈 React Hooks 的逻辑抽象与封装
- centos创建逻辑卷和扩容逻辑卷
- AI「王道」逻辑编程的复兴?清华提出神经逻辑机,已入选ICLR
- 内聚代码提高逻辑可读性,用MCVP接续你的大逻辑
- 逻辑式编程语言极简实现(使用C#) - 1. 逻辑式编程语言介绍
- C#列表到列表转换
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
程序员2005精华本
《程序员》杂志社 / 电子工业 / 2006-1 / 45.00元
本书为集结了《程序员》杂志与《msdn开发精选》杂志精华。分上、下两册,内容包括人物&报道、管理与实践、程序员手册、年鉴、《程序员》技术专题、《msdn开发精选》文章精选等。一起来看看 《程序员2005精华本》 这本书的介绍吧!