前言
RecyclerView是一個大家常用的列表控件,在列表中不免會出現多種類型的布局,這時adapter中多種類型的判斷就會充滿著switch的壞味道,可怕的是需求變更,增加或修改新的類型時,所有的改動都在adapter中進行,沒有一個良好的擴展性。
MutliItem主要就是解決這些問題,在正常使用中做到了Adapter零編碼,解放了復雜的Adapter類,本庫提供了多類型和ViewHolder創(chuàng)建綁定的管理器,這樣Adapter通過依賴倒置與列表中的多類型解耦,還提高了擴展性。在本庫中不同實體類可以直接當成數據源綁定到adapter中,你不用去擔心item type的計算,并且對每種類型的ViewHolder也做到了隔離。
本庫的定位并不是大而全,但是會盡量做到簡單易用。
源碼地址
Github地址:MultiItem,請大家多多關注,更多更新會首先在GitHub上體現,也會在第一時間在本平臺發(fā)布
效果截圖


下一步要做什么
- DataBinding特性支持
- 錄入界面的復用和封裝的demo代碼(錄入業(yè)務較多同學可以多多關注)
- 思考動畫分割線等一些功能封裝
用法
添加依賴
- 配置gradle:
在Project root的build.gradle中添加:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
在Module中添加(最新版本請在源碼地址查看):
dependencies {
compile 'com.github.free46000:MultiItem:0.9.3'
}
- 或者你也可以直接克隆源碼
多種類型列表用法
這里由于單一和多種類型寫法上沒有差別,所以就不單獨貼出單一類型的列表代碼了。
注冊多種類型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);
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;
}
}
至此本庫的多種類型列表用法已經完成,并沒有修改或繼承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判斷是否本人發(fā)送并返回對應ViewHolderManager的index值
return itemData.getSender().equals(uid) ? 0 : 1;
}
});
recyclerView.setAdapter(adapter);
設置點擊監(jiān)聽
點擊監(jiān)聽:
adapter.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(BaseViewHolder viewHolder) {
//通過viewHolder獲取需要的數據
toastUser(String.format("你點擊了第%s位置的數據:%s", viewHolder.getItemPosition()
, viewHolder.getItemData()));
}
});
長按監(jiān)聽:
adapter.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public void onItemLongClick(BaseViewHolder viewHolder) {
//通過viewHolder獲取需要的數據
toastUser(String.format("你長按了第%s位置的數據:%s", viewHolder.getItemPosition()
, viewHolder.getItemData()));
}
});
詳解
主要流程
- 為指定的數據源注冊
ViewHolderManager提供視圖創(chuàng)建綁定等工作 - 在列表創(chuàng)建的過程中通過數據源在
ItemTypeManager找到對應的ViewHolderManager - 按照需要創(chuàng)建與刷新視圖并對視圖做一些通用處理
ViewHolder管理
ViewHolder管理源碼類為ViewHolderManager,使用者會首先注冊數據源和本實例的對應關系,由類型管理類提供統(tǒng)一管理。
- 提供了參數類,會在
adapter調用本類方法的時候傳入并做出通用處理 - 本類的設計使用泛型,是為了在后續(xù)回調方法中有更直觀的類型體現,這也是強類型和泛型帶來的好處,給人在編寫代碼的時候帶來確定感
- 本類為抽象類需要重寫
ViewHolder的創(chuàng)建與綁定方法,為了方便后續(xù)使用,寫了一個簡單的BaseViewHolderManager實現類,請讀者根據業(yè)務自行決定是否需要使用更靈活的基類,這里貼出需要復寫的兩個方法,延續(xù)了Adapter中的命名規(guī)則,在使用中減少一些認知成本:
/**
* 創(chuàng)建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);
ViewHolder管理組合(相同數據源對應多個ViewHolderManager)
組合管理源碼類為ViewHolderManagerGroup,本實例需要一個ViewHolderManager集合,并增加通過數據源指定哪個ViewHolderManager的方法,使用者同樣會注冊數據源和本實例的對應關系,由類型管理類對本類中的ViewHolderManager集合進行統(tǒng)一注冊管理。下面貼出關鍵代碼:
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);
類型管理
類型管理源碼類為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);
}
- 數據源一對多
viewHolderManager時,關鍵代碼:
/**
* 通過group獲取一組ViewHolderManager循環(huán)注冊,并生成不同的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);
}
希望大家會喜歡,多多留言交流