項目中常常性會碰到列表的單選、多選,實現起來好像也不難,可是最近項目有好多個須要單選/多選的頁面,看到設計稿的一瞬間,腦子靈光一閃,爲啥不把這些簡單而又繁瑣的邏輯給封裝起來呢(懶癌發做)?git
因而就有了下面的小東西(開源庫)...github
Adapter.notifyItemChange
方法,因此不會有閃屏Bug廢話少說,先上圖:bash
首先在你的工程根目錄的 gradle 文件下添加如下配置:app
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
複製代碼
接着就能夠在你的moudle,通常是 app 的 gradle 文件下添加依賴:maven
implementation 'com.github.gminibird:CheckHelper:1.0'
複製代碼
最後那個1.0是版本號,能夠上 GitHub 上看最新的,而後就能夠愉快的玩耍啦。ide
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);
}
});
複製代碼
@Override
protected void onBind(@NonNull LwViewHolder holder, @NonNull String item) {
//這裏用了本身封裝的Adapter,至關於onBindViewHolder方法
mCheckHelper.bind(item, holder, holder.itemView);
}
複製代碼
而後,而後就完成了。。。運行就能夠看到想要的效果,選中的數據能夠調用相應CheckHelper
實例的getXXX()
獲取。gradle
總的原理其實很簡單,就是對應的 CheckHelper 實現類內部維護一些選中的數據。下面細說下具體實現:ui
CheckHelper
CheckHelper
是目前兩個實現類的基類,提供了一些基礎的公共功能,好比設置監聽器,註冊選擇器等,其實就是一個模板模式,把公共部分以及執行順序都定了,而後交由子類完成具體的數據增改。this
裏面有兩個重要的方法,一個是bind()
,另外一個是 select()
,分別對應了onBindViewHolder
以及onClick
(點擊)方法:url
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
方法裏面也是作了一系列回調,基本和上面的方法一致。因此子類只要重寫這兩個方法,並實現相應的數據操做便可。
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);
}
}
複製代碼
接着就回調父類的一系列回調接口了。
多選類主要是靠一個 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
源碼戳上面 ^^^
- 曾榮基:廣州蘆葦科技 APP 團隊 Android 開發工程師
- 咱們正在招募小夥伴,有興趣的小夥伴能夠把簡歷發到 app@talkmoney.cn,備註:來自掘金社區
- 詳情能夠戳這裏--> 廣州蘆葦信息科技