MultiItem用法與詳解-優雅的實現多類型RecyclerView Adapter

前言

RecyclerView是一個你們經常使用的列表控件,在列表中難免會出現多種類型的佈局,這時adapter中多種類型的判斷就會充滿着switch的壞味道,可怕的是需求變動,增長或修改新的類型時,全部的改動都在adapter中進行,沒有一個良好的擴展性。
MutliItem主要就是解決這些問題,在正常使用中作到了Adapter零編碼,解放了複雜的Adapter類,本庫提供了多類型和ViewHolder建立綁定的管理器,這樣Adapter經過依賴倒置與列表中的多類型解耦,還提升了擴展性。在本庫中不一樣實體類能夠直接當成數據源綁定到adapter中,你不用去擔憂item type的計算,而且對每種類型的ViewHolder也作到了隔離。
本庫的定位並非大而全,可是會盡可能作到簡單易用。java

源碼地址

Github地址:MultiItem,請你們多多關注,更多更新會首先在GitHub上體現,也會在第一時間在本平臺發佈android

效果截圖

multi_item
multi_item
chat
chat

用法

添加依賴

  • 配置gradle:

Project rootbuild.gradle中添加:git

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}複製代碼

Module中添加(最新版本請在源碼地址查看):github

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類,其餘類類似,下面貼出本類所有代碼,是否是很是清晰:bash

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便可。maven

相同數據源對應多個ViewHolder(聊天界面)

這是一種特殊的需求,須要在運行時經過數據源中的某個屬性,判斷加載的佈局,典型的就是聊天功能,相同消息數據對應左右兩種氣泡視圖,在此處貼出註冊時的關鍵代碼,其餘和多種類型列表相似:ide

//初始化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()));
    }
});複製代碼

長按監聽:gradle

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 ListviewHolderManager 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);
}複製代碼

但願你們會喜歡,多多留言交流

相關文章
相關標籤/搜索