項目中常常性會碰到列表的單選、多選,實現起來好像也不難,可是最近項目有好多個須要單選/多選的頁面,看到設計稿的一瞬間,腦子靈光一閃,爲啥不把這些簡單而又繁瑣的邏輯給封裝起來呢(懶癌發做)?git
因而就有了下面的小東西(開源庫)...github
Adapter.notifyItemChange
方法,因此不會有閃屏Bug廢話少說,先上圖:bash
首先在你的工程根目錄的 gradle 文件下添加如下配置:markdown
allprojects { repositories { ... maven { url 'https://jitpack.io' } } } 複製代碼
接着就能夠在你的moudle,通常是 app 的 gradle 文件下添加依賴:app
implementation 'com.github.gminibird:CheckHelper:1.0' 複製代碼
最後那個1.0是版本號,能夠上 GitHub 上看最新的,而後就能夠愉快的玩耍啦。maven
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()
獲取。ide
總的原理其實很簡單,就是對應的 CheckHelper 實現類內部維護一些選中的數據。下面細說下具體實現:oop
CheckHelper
CheckHelper
是目前兩個實現類的基類,提供了一些基礎的公共功能,好比設置監聽器,註冊選擇器等,其實就是一個模板模式,把公共部分以及執行順序都定了,而後交由子類完成具體的數據增改。gradle
裏面有兩個重要的方法,一個是bind()
,另外一個是 select()
,分別對應了onBindViewHolder
以及onClick
(點擊)方法:this
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,備註:來自掘金社區
- 詳情能夠戳這裏--> 廣州蘆葦信息科技