MultiItem 用法與詳解 - 優雅的實現多類型 RecyclerView Adapter
前言
RecyclerView 是一個大家常用的列表控件,在列表中不免會出現多種類型的布局,這時 adapter 中多種類型的判斷就會充滿著 switch 的壞味道,可怕的是需求變更,增加或修改新的類型時,所有的改動都在 adapter 中進行,沒有一個良好的擴展性。
MutliItem 主要就是解決這些問題,在正常使用中做到了 Adapter 零編碼,解放了復雜的 Adapter 類,本庫提供了多類型和 ViewHolder 創建綁定的管理器,這樣 Adapter 通過依賴倒置與列表中的多類型解耦,還提高了擴展性。在本庫中不同實體類可以直接當成數據源綁定到 adapter 中,你不用去擔心 item type 的計算,并且對每種類型的 ViewHolder 也做到了隔離。
本庫的定位并不是大而全,但是會盡量做到簡單易用。
源碼地址
Github地址: MultiItem ,請大家多多關注,更多更新會首先在GitHub上體現,也會在第一時間在本平臺發布
效果截圖
multi_item
chat
下一步要做什么
- 任務面板 跨 RecyclerView 的 Item 拖動 支持雙擊縮小后拖動(仿協同辦公軟件)
- 增加 header footer line 等一些可復用視圖
- DataBinding特性支持
- 錄入界面的復用和封裝的demo代碼(錄入業務較多同學可以多多關注)
- 思考動畫分割線等一些功能封裝
用法
添加依賴
- 配置gradle:
在 Project root 的 build.gradle 中添加:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
在 Module 中添加:
dependencies {
compile 'com.github.free46000:MultiItem:0.9.1'
}
- 或者你也可以直接克隆源碼
多種類型列表用法
這里由于單一和多種類型寫法上沒有差別,所以就不單獨貼出單一類型的列表代碼了。
注冊多種類型 ViewHolderManager ,并為 adapter 設置多種類型數據源:
//初始化adapter
BaseItemAdapter adapter = new BaseItemAdapter();
//為TextBean數據源注冊ViewHolderManager管理類
adapter.register(TextBean.class, new TextViewManager());
//為更多數據源注冊ViewHolderManager管理類
adapter.register(ImageTextBean.class, new ImageAndTextManager());
adapter.register(ImageBean.class, new ImageViewManager());
//組裝數據源list
List<Object> list = new ArrayList<>();
list.add(new TextBean("AAA"));
list.add(new ImageBean(R.drawable.img1));
list.add(new ImageTextBean(R.drawable.img2, "BBB" + i));
//為adapter注冊數據源list
adapter.setDataItems(list);
recyclerView.setAdapter(adapter);</code></pre>
ViewHolder 管理類的子類 TextViewManager 類,其他類相似,下面貼出本類全部代碼,是不是非常清晰:
public class ImageViewManager extends BaseViewHolderManager<ImageBean> {
@Override
public void onBindViewHolder(BaseViewHolder holder, ImageBean data) {
//在指定viewHolder中獲取控件為id的view
ImageView imageView = getView(holder, R.id.image);
imageView.setImageResource(data.getImg());
}
@Override
protected int getItemLayoutId() {
//返回item布局文件id
return R.layout.item_image;
}
}</code></pre>
至此本庫的多種類型列表用法已經完成,并沒有修改或繼承 RecyclerView Adapter 類,完全使用默認實現 BaseItemAdapter 即可。
相同數據源對應多個ViewHolder(聊天界面)
這是一種特殊的需求,需要在運行時通過數據源中的某個屬性,判斷加載的布局,典型的就是聊天功能,相同消息數據對應左右兩種氣泡視圖,在此處貼出注冊時的關鍵代碼,其他和多種類型列表類似:
//初始化adapter
BaseItemAdapter adapter = new BaseItemAdapter();
//為XXBean數據源注冊XXManager管理類組合
adapter.register(MessageBean.class, new ViewHolderManagerGroup<MessageBean>(new SendMessageManager(), new ReceiveMessageManager()) {
@Override
public int getViewHolderManagerIndex(MessageBean itemData) {
//根據message判斷是否本人發送并返回對應ViewHolderManager的index值
return itemData.getSender().equals(uid) ? 0 : 1;
}
});
recyclerView.setAdapter(adapter);</code></pre>
設置點擊監聽
點擊監聽:
adapter.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(BaseViewHolder viewHolder) {
//通過viewHolder獲取需要的數據
toastUser(String.format("你點擊了第%s位置的數據:%s", viewHolder.getItemPosition()
, viewHolder.getItemData()));
}
});
長按監聽:
adapter.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public void onItemLongClick(BaseViewHolder viewHolder) {
//通過viewHolder獲取需要的數據
toastUser(String.format("你長按了第%s位置的數據:%s", viewHolder.getItemPosition()
, viewHolder.getItemData()));
}
});
詳解
主要流程
- 為指定的數據源注冊 ViewHolderManager 提供視圖創建綁定等工作
- 在列表創建的過程中通過數據源在 ItemTypeManager 找到對應的 ViewHolderManager
- 按照需要創建與刷新視圖并對視圖做一些通用處理
ViewHolder管理
ViewHolder管理源碼類為 ViewHolderManager ,使用者會首先注冊數據源和本實例的對應關系,由類型管理類提供統一管理。
- 提供了參數類,會在 adapter 調用本類方法的時候傳入并做出通用處理
- 本類的設計使用泛型,是為了在后續回調方法中有更直觀的類型體現,這也是強類型和泛型帶來的好處,給人在編寫代碼的時候帶來確定感
- 本類為抽象類需要重寫 ViewHolder 的創建與綁定方法,為了方便后續使用,寫了一個簡單的 BaseViewHolderManager 實現類,請讀者根據業務自行決定是否需要使用更靈活的基類,這里貼出需要復寫的兩個方法,延續了 Adapter 中的命名規則,在使用中減少一些認知成本:
/**
- 創建ViewHolder
- {@link android.support.v7.widget.RecyclerView.Adapter#onCreateViewHolder}
*/
@NonNull
public abstract V onCreateViewHolder(@NonNull ViewGroup parent);
/**
- 為ViewHolder綁定數據
- {@link android.support.v7.widget.RecyclerView.Adapter#onBindViewHolder}
*
- @param t 數據源
*/
public abstract void onBindViewHolder(@NonNull V holder, @NonNull T t);</code></pre>
ViewHolder管理組合(相同數據源對應多個ViewHolderManager)
組合管理源碼類為 ViewHolderManagerGroup ,本實例需要一個 ViewHolderManager 集合,并增加通過數據源指定哪個 ViewHolderManager 的方法,使用者同樣會注冊數據源和本實例的對應關系,由類型管理類對本類中的 ViewHolderManager 集合進行統一注冊管理。下面貼出關鍵代碼:
private ViewHolderManager[] viewHolderManagers;
/**
- @param viewHolderManagers 相同數據源對應的所有ViewHolderManager
*/
public ViewHolderManagerGroup(ViewHolderManager... viewHolderManagers) {
if (viewHolderManagers == null || viewHolderManagers.length == 0) {
throw new IllegalArgumentException("viewHolderManagers can not be null");
}
this.viewHolderManagers = viewHolderManagers;
}
/**
- 根據item數據源中的屬性判斷應該返回的對應viewHolderManagers的index值
*
- @param itemData item數據源
- @return index值應該是在viewHolderManagers數組有效范圍內
*/
public abstract int getViewHolderManagerIndex(T itemData);</code></pre>
類型管理
類型管理源碼類為 ItemTypeManager ,通過數據源 className List 和 viewHolderManager List 兩組集合對類型進行管理,并對 Adapter 提供注冊和對應關系查找等方法的支持,這里并沒有把這個地方設計靈活,如果有一些變化還是希望可以在 ViewHolderManager 做出適配。
- 數據源一對一 viewHolderManager 時比較簡單,關鍵代碼:
/**
- 通過數據源
className List
和viewHolderManager List
兩組集合對類型進行管理
*
- @param cls 數據源class
- @param manager ViewHolderManager
- @see com.freelib.multiitem.adapter.BaseItemAdapter#register(Class, ViewHolderManager)
*/
public void register(Class<?> cls, ViewHolderManager manager) {
register(getClassName(cls), manager);
}</code></pre>
- 數據源一對多 viewHolderManager 時,關鍵代碼:
/**
- 通過group獲取一組ViewHolderManager循環注冊,并生成不同的className作為標識<br>
- 其他類似{@link #register(Class, ViewHolderManager)}
*
- @param cls 數據源class
- @param group 多個ViewHolderManager的組合
- @see com.freelib.multiitem.adapter.BaseItemAdapter#register(Class, ViewHolderManagerGroup)
*/
public void register(Class<?> cls, ViewHolderManagerGroup group) {
ViewHolderManager[] managers = group.getViewHolderManagers();
for (int i = 0, length = managers.length; i < length; i++) {
register(getClassNameFromGroup(cls, group, managers[i]), managers[i]);
}
itemClassNameGroupMap.put(getClassName(cls), group);
}</code></pre> 希望大家會喜歡,多多留言交流
來自:https://juejin.im/post/58d1d9d0128fe1006ca69b45