前言
RecyclerView
是一個大家常用的列表控制元件,在列表中不免會出現多種型別的佈局,這時adapter
中多種型別的判斷就會充滿著switch
的壞味道,可怕的是需求變更,增加或修改新的型別時,所有的改動都在adapter
中進行,沒有一個良好的擴充套件性。MutliItem
主要就是解決這些問題,在正常使用中做到了Adapter
零編碼,解放了複雜的Adapter
類,本庫提供了多型別和ViewHolder
建立繫結的管理器,這樣Adapter
通過依賴倒置與列表中的多型別解耦,還提高了擴充套件性。在本庫中不同實體類可以直接當成資料來源繫結到adapter
中,你不用去擔心item type
的計算,並且對每種型別的ViewHolder
也做到了隔離。
本庫的定位並不是大而全,但是會盡量做到簡單易用。
原始碼地址
Github地址:MultiItem,請大家多多關注,更多更新會首先在GitHub上體現,也會在第一時間在本平臺釋出
效果截圖
用法
新增依賴
- 配置gradle:
在Project root
的build.gradle
中新增:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}複製程式碼
在Module
中新增(最新版本請在原始碼地址檢視):
dependencies {
compile 'com.github.free46000:MultiItem:0.9.7'
}複製程式碼
- 或者你也可以直接克隆原始碼
多種型別列表用法
這裡由於單一和多種型別寫法上沒有差別,所以就不單獨貼出單一型別的列表程式碼了。
註冊多種型別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判斷是否本人傳送並返回對應ViewHolderManager的index值
return itemData.getSender().equals(uid) ? 0 : 1;
}
});
recyclerView.setAdapter(adapter);複製程式碼
設定點選監聽
點選監聽:
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);複製程式碼
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);複製程式碼
型別管理
型別管理原始碼類為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迴圈註冊,並生成不同的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);
}複製程式碼
希望大家會喜歡,多多留言交流