Android DataBinding使用总结(五)结合MultiType展示多类型列表

一、前言:

上一篇:Android DataBinding的使用 (四) 多类型列表展示
https://www.jianshu.com/p/951b196b752c

gitHub 地址:https://github.com/lyyRunning/DataBindingDemo

今天主要是学习一下如何展示通过另外一种方式实现RecyclerView多类型列表,效果如下:(为了方便,不同的item采取不同的背景颜色)

image.png

二、MultiType库简介

drakeet/MultiType(点我查看Github源码),是GitHub上开源的实现多类型列表的一个第三方库,中文文档如下:

使用方式如下:

1. 步骤一:

创建一个 class,它将是你的数据类型或 Java bean / model. 对这个类的内容没有任何限制。示例如下:

public class Category {

    @NonNull public final String text;

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

2. 步骤二:

创建一个 class 继承 ItemViewBinder.

ItemViewBinder 是个抽象类,其中 onCreateViewHolder 方法用于生产你的 Item View Holder, onBindViewHolder 用于绑定数据到 Views. 一般一个 ItemViewBinder 类在内存中只会有一个实例对象,MultiType 内部将复用这个 binder 对象来生产所有相关的 item views 和绑定数据。示例:

public class CategoryViewBinder
    extends ItemViewBinder {

    @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);
        }
    }
}

3. 步骤三:

在 Activity 中加入 RecyclerView 和 List 并注册你的类型,示例:

public class MainActivity extends AppCompatActivity {

    private MultiTypeAdapter adapter;

    /* Items 等同于 ArrayList */
    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库进行多类型列表的实现。

三、MultiType库的优点:

1. 我为什么要学习这个库?

可以理解,我们可能已经学习了很多种RecyclerView实现多类型列表的方式,我们有什么必要去多花费时间成本学习?

——何况我们已经知道如何通过DataBinding进行多类型列表的展示了。

我们先归纳一下RecyclerView多类型列表展示的方法:

1、普通方式实现多类型列表,较简单,好理解;
2、面向接口,实现耦合性极低的多类型列表;
3、DataBinding实现可多次复用的Adapter,从而实现多类型列表
4、其他方式

上述前两种实现方式,可以通过我的 RecyclerView3-面向接口优雅地实现多类型列表

文中进行了解;第三种方式可以通过我的上一篇文章Android DataBinding使用总结(四) DataBinding的多类型列表展示 进行简单了解。

抛开其他不谈,单说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、数据源(Student)

public class Student extends BaseObservable implements IMulTypeBindingBean {

    public Student(String name, int age) {
        this.name.set(name);
        this.age = age;
    }

    /**
     * ObservableObject 实现数据绑定
     * 这里最好是private,否则在xml的代码提示会有两个age(1,成员变量的直接引用 2,代码模板生成),虽然都能用,但强迫症不能忍
     */
    private int age;

    @Bindable
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
        notifyPropertyChanged(BR.age);
    }

    /**
     * ObservableFields 实现数据绑定(推荐)
     */
    public final ObservableField name = new ObservableField<>();



    /**
     * IMulTypeBindingBean接口的实现方法
     *
     * 使用方法请见DataBinding多类型列表Activity的展示(该接口方法仅在该界面使用)
     * {@link A04MulTypeRecyclerBindActivity}
     *
     * @return 如果30岁以下和30岁以上,数据展示在不同的layout上(背景色不同)
     */
    @Override
    public int getLayoutResouse() {
        if (age <= 30) {
            return R.layout.item_recycler_view1;
        } else {
            return R.layout.item_recycler_view2;
        }
    }

2、xml 文件

我们列出两种不同item的xml布局文件:

  1. item_multitype_library_view1.xml



    

        

        

    

    

        
  1. item_multitype_library_view2.xml



    

        

        

    

    

        

3、创建BaseViewHolder

public class BaseViewHolder 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>{

    @NonNull
    @Override
    protected BaseViewHolder 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 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>{

    @NonNull
    @Override
    protected BaseViewHolder 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 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 class A05MultiTypeStudyActivity extends AppCompatActivity {

    private ActivityRecyclerBindBinding binding;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = DataBindingUtil.setContentView(this, R.layout.activity_recycler_bind);

//        showSingleList();
        showMultiTypeList();
    }

    /**
     * 展示简单列表
     */
    public void showSingleList(){
        MultiTypeAdapter adapter = new MultiTypeAdapter();
        adapter.register(Student.class, new MultiTypeBinder());
        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 Student("xiaoming", 29));
            items.add(new Student("qingmei2", 18));
        }
        Log.i("tag", "items.size() " + items.size());
        adapter.setItems(items);
        adapter.notifyDataSetChanged();
    }

    /**
     * 展示多类型列表
     */
    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();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_multi_type_library,menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()){
            case R.id.singleList:
                showSingleList();
                break;
            case R.id.multiTypeList:
                showMultiTypeList();
                break;
        }
        return super.onOptionsItemSelected(item);
    }
}

五、总结

需求实现,我们可以看到,如果我们有item需求变动,修改的时候只需要找到对应的MultiTypeBinder类修改即可,无论是layout文件或者响应事件,亦或者添加其他需求在onCreateViewHolder()&onBindViewHolder()方法中,拓展性都非常优秀;而且只需要修改这一个类,耦合性更低。


原文链接:https://blog.csdn.net/mq2553299/article/details/72834467

你可能感兴趣的:(Android DataBinding使用总结(五)结合MultiType展示多类型列表)