MultiItem用法與詳解-優(yōu)雅的實現多類型RecyclerView Adapter

前言

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

源碼地址

Github地址:MultiItem,請大家多多關注,更多更新會首先在GitHub上體現,也會在第一時間在本平臺發(fā)布

效果截圖

multi_item
multi_item
chat
chat

下一步要做什么

  • DataBinding特性支持
  • 錄入界面的復用和封裝的demo代碼(錄入業(yè)務較多同學可以多多關注)
  • 思考動畫分割線等一些功能封裝

用法

添加依賴

  • 配置gradle:

Project rootbuild.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 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循環(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);
}

希望大家會喜歡,多多留言交流

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容