1. 程式人生 > >Android DataBinding使用總結(五)結合MultiType展示多型別列表

Android DataBinding使用總結(五)結合MultiType展示多型別列表

前言

在我的前幾篇文章中,簡單學習了以下內容:

我們在上文中通過自己動手實現BaseMulTypeAdapter類,從而達到了一次實現,多次複用的效果,大概流程為:

1.建立不同型別item的xml佈局檔案
2.使對應的資料類(javaBean)實現IMulTypeBindingBean介面,在介面中返回該資料對應item型別的佈局ID;
3.建立BaseViewHolder和BaseMulTypeAdapter,之後我們在實現列表時,再也不需要實現Adapter和ViewHolder,只需要複用即可
4.Activity中初始化RecyclerView,設定Adapter和LayoutManager

今天主要是學習一下如何展示通過另外一種方式實現RecyclerView多型別列表,效果如下:(為了方便,不同的item採取不同的背景顏色)

這裡寫圖片描述

一、MultiType庫簡介

使用方式如下:

Step 1

建立一個 class,它將是你的資料型別或 Java bean / model. 對這個類的內容沒有任何限制。示例如下:

public class Category {

    @NonNull public final String text;

    public Category(@NonNull String text) {
        this
.text = text; } }

Step 2

建立一個 class 繼承 ItemViewBinder.

ItemViewBinder 是個抽象類,其中 onCreateViewHolder 方法用於生產你的 Item View Holder, onBindViewHolder 用於繫結資料到 Views. 一般一個 ItemViewBinder 類在記憶體中只會有一個例項物件,MultiType 內部將複用這個 binder 物件來生產所有相關的 item views 和繫結資料。示例:

public class CategoryViewBinder
    extends
ItemViewBinder<Category, CategoryViewBinder.ViewHolder> {
@NonNull @Override protected ViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) { View root = inflater.inflate(R.layout.item_category, parent, false); return new ViewHolder(root); } @Override protected void onBindViewHolder(@NonNull ViewHolder holder, @NonNull Category category) { holder.category.setText(category.text); } static class ViewHolder extends RecyclerView.ViewHolder { @NonNull private final TextView category; ViewHolder(@NonNull View itemView) { super(itemView); this.category = (TextView) itemView.findViewById(R.id.category); } } }

Step 3

在 Activity 中加入 RecyclerView 和 List 並註冊你的型別,示例:

public class MainActivity extends AppCompatActivity {

    private MultiTypeAdapter adapter;

    /* Items 等同於 ArrayList<Object> */
    private Items items;

    @Override 
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);

        adapter = new MultiTypeAdapter();

        /* 註冊型別和 View 的對應關係 */
        adapter.register(Category.class, new CategoryViewBinder());
        adapter.register(Song.class, new SongViewBinder());
        recyclerView.setAdapter(adapter);

        /* 模擬載入資料,也可以稍後再載入,然後使用
         * adapter.notifyDataSetChanged() 重新整理列表 */
        items = new Items();
        for (int i = 0; i < 20; i++) {
            items.add(new Category("Songs"));
            items.add(new Song("小艾大人", R.drawable.avatar_dakeet));
            items.add(new Song("許岑", R.drawable.avatar_cen));
        }
        adapter.setItems(items);
        adapter.notifyDataSetChanged();
    }
}

大功告成!這就是 MultiType 的基礎用法了。其中 onCreateViewHolder 和 onBindViewHolder 方法名沿襲了使用 RecyclerView 的習慣,令人一目瞭然,減少了新人的學習成本。

上述為作者為我們提供普通方式實現多型別列表的步驟,接下來我們通過DataBinding結合作者的MultiType庫進行多型別列表的實現。

二、轉折

在學習之前,會不會有這樣一種想法:

我為什麼要學習這個庫?

可以理解,我們可能已經學習了很多種RecyclerView實現多型別列表的方式,我們有什麼必要去多花費時間成本學習?

——何況我們已經知道如何通過DataBinding進行多型別列表的展示了。

我們先歸納一下RecyclerView多型別列表展示的方法:

1、普通方式實現多型別列表,較簡單,好理解
2、面向介面,實現耦合性極低的多型別列表;
3、DataBinding實現可多次複用的Adapter,從而實現多型別列表
4、其他方式

拋開其他不談,單說DataBinding實現多型別列表,我們從上一篇文中可以得知,我們僅僅需要了了數行程式碼即可實現多型別列表:

 /**
   * 幾行程式碼展示多型別列表
   */

    MulTypeBindAdapter adapter = new MulTypeBindAdapter(students);
    adapter.setItemPresenter(new MulRecyclerBindPresenterI());

    binding.recyclerView
            .setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
    binding.recyclerView
            .setAdapter(adapter);

對於不同的item,我們在Adapter中僅需要傳入資料來源(資料來源中不同型別資料已繫結不同型別的item佈局檔案),響應事件我們只需要實現Presenter介面並傳入即可。

關於這個庫,我的理解是「靈活 」。

套用作者的話:

MultiType 設計伊始,我給它定了幾個原則:

1、要簡單,便於他人閱讀程式碼

因此我極力避免將它複雜化,避免加入許多不相干的內容。我想寫人人可讀的程式碼,使用簡單的方式,去實現複雜的需求。過多不相干、沒必要的程式碼,將會使專案變得令人暈頭轉向,難以閱讀,遇到需要定製、解決問題的時候,無從下手。

2、要靈活,便於拓展和適應各種需求

很多人會得意地告訴我,他們把 MultiType 原始碼精簡成三四個類,甚至一個類,以為程式碼越少就是越好,這我不能贊同。MultiType 考慮得更遠,這是一個提供給大眾使用的類庫,過度的精簡只會使得大幅失去靈活性。它或許不是使用起來最簡單的,但很可能是使用起來最靈活的。 在我看來,”直觀”、”靈活”優先順序大於”簡單”。因此,MultiType 以介面或抽象進行連線,這意味著它的角色、元件都可以被替換,或者被拓展和繼承。如果你覺得它使用起來還不夠簡單,完全可以通過繼承封裝出更具體符合你使用需求的方法。它已經暴露了足夠豐富、周到的介面以供拓展,我們不應該直接去修改原始碼,這會導致一旦後續發現你的精簡版滿足不了你的需求時,已經沒有回頭路了。

3、要直觀,使用起來能令專案程式碼更清晰可讀,一目瞭然

MultiType 提供的 ItemViewBinder 沿襲了 RecyclerView Adapter 的介面命名,使用起來更加舒適,符合習慣。另外,MultiType 很多地方放棄使用反射而是讓使用者顯式指明一些關係,如:MultiTypeAdapter#register 方法,需要傳遞一個數據模型 class 和 ItemViewBinder 物件,雖然有很多方法可以把它精簡成單一引數方法,但我們認為顯式宣告資料模型類與對應關係,更具直觀。

直接通過上文中的方式實現多型別列表,沒有任何問題,但是耦合性相對較高,首先,如果我們更改需求,那麼我們要修改資料來源(JavaBean.class中的IMulTypeBindingBean介面),還需要修改Presenter中對應的回撥方法,也就是說,我們除了xml檔案,還需要修改至少兩個類。

所幸和普通方式實現方式不同,我們至少並不需要修改Adapter和ViewHolder…… 0 0

那麼我們通過MultiType這個庫實現之後呢,需要修改幾個類?

答案是:一個

三、程式碼

如果你已經親手實踐過這個庫,或者瞭解了這個庫的使用方式,那麼我們開始吧。

1、資料來源(JavaBean)

和上一篇文中不同,我們不需要修改任何東西,或者實現什麼介面。

2、xml檔案

我們列出兩種不同item的xml佈局檔案:

1、item_multitype_library_view1.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="itemPresenter"
            type="com.mei_husky.samplemvvm.view.adapter.binder.MultiTypeBinder1.RecyclerBindPresenter" />

        <variable
            name="data"
            type="com.mei_husky.samplemvvm.model.Student" />

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:background="#ff0"
        android:orientation="horizontal">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="16dp"
            android:gravity="center_vertical"
            android:textAllCaps="false"
            android:text="@{`Student Name : `+ data.name.get()}" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="16dp"
            android:textAllCaps="false"
            android:gravity="center_vertical"
            android:text="@{`Student Age : `+data.age}" />

    </LinearLayout>
</layout>

2、item_multitype_library_view2.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="itemPresenter"
            type="com.mei_husky.samplemvvm.view.adapter.binder.MultiTypeBinder2.RecyclerBindPresenter" />

        <variable
            name="data"
            type="String" />

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:orientation="horizontal">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="16dp"
            android:gravity="center_vertical"
            android:textAllCaps="false"
            android:text="@{`String Name : `+ data}" />


    </LinearLayout>
</layout>

3、建立BaseViewHolder

和前兩篇文章中一模一樣的BaseViewHolder:

public class BaseViewHolder<T extends ViewDataBinding> extends RecyclerView.ViewHolder {

    protected final T binding;

    public BaseViewHolder(T t) {
        super(t.getRoot());
        this.binding = t;
    }

    public T getBinding() {
        return binding;
    }

}

4、每一個不同型別的item,對應一個ItemViewBinder的子類:

//第一種item
public class MultiTypeBinder1 extends ItemViewBinder<Student,BaseViewHolder<ItemMultitypeLibraryView1Binding>>{

    @NonNull
    @Override
    protected BaseViewHolder<ItemMultitypeLibraryView1Binding> onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
        ItemMultitypeLibraryView1Binding binding = DataBindingUtil.inflate(inflater,R.layout.item_multitype_library_view1, parent, false);

        return new BaseViewHolder(binding);
    }

    @Override
    protected void onBindViewHolder(@NonNull BaseViewHolder<ItemMultitypeLibraryView1Binding> holder, @NonNull Student item) {
        holder.getBinding().setData(item);
        holder.getBinding().executePendingBindings();
    }


    //對於不同的響應事件,我們可以這樣
     public class RecyclerBindPresenter implements IBaseBindingPresenter {

        public void onNameClick(Student student) {
            //TODO
        }
    }
}

//第二種item
public class MultiTypeBinder2 extends ItemViewBinder<String,BaseViewHolder<ItemMultitypeLibraryView2Binding>>{

    @NonNull
    @Override
    protected BaseViewHolder<ItemMultitypeLibraryView2Binding> onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
        ItemMultitypeLibraryView2Binding binding = DataBindingUtil.inflate(inflater,R.layout.item_multitype_library_view2, parent, false);
        return new BaseViewHolder(binding);
    }

    @Override
    protected void onBindViewHolder(@NonNull BaseViewHolder<ItemMultitypeLibraryView2Binding> holder, @NonNull String item) {
        holder.getBinding().setData(item);
        holder.getBinding().executePendingBindings();
    }

    //對於不同的響應事件,我們可以這樣
     public class RecyclerBindPresenter implements IBaseBindingPresenter {

        public void onNameClick(Student student) {
            //TODO
        }
    }
}

5、Activity中使用

    /**
     * 展示多型別列表
     */
    public void showMultiTypeList(){
        MultiTypeAdapter adapter = new MultiTypeAdapter();
        adapter.register(Student.class,new MultiTypeBinder1());
        adapter.register(String.class,new MultiTypeBinder2());
        binding.recyclerView.setAdapter(adapter);
        binding.recyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));

        Items items = new Items();
        for (int i = 0; i < 10; i++) {
            items.add(new String("Songs"));
            items.add(new Student("xiaoming", 29));
            items.add(new Student("qingmei2", 18));
        }
        adapter.setItems(items);
        adapter.notifyDataSetChanged();
    }

總結

需求實現,我們可以看到,如果我們有item需求變動,修改的時候只需要找到對應的MultiTypeBinder類修改即可,無論是layout檔案或者響應事件,亦或者新增其他需求在onCreateViewHolder()&onBindViewHolder()方法中,拓展性都非常優秀;而且只需要修改這一個類,耦合性更低。

我的下一篇文章將會進行介紹:

參考文件

本文原始碼地址

該demo包含本文中所有程式碼但不僅限於:

使用了 Mvvm+DataBinding 搭建的DemoApp

——DataBinding的入門使用以及引導介面

——Databinding的使用

——RecyclerView中使用dataBinding進行列表展示

——RecyclerView中使用dataBinding展示多型別列表

——MultiType庫+DataBinding展示RecyclerView多型別列表

——簡單搭建MVVM+DataBinding+Dagger2的介面