RecyclerView的超强辅助Graywater——基础实操篇

关于Graywater的系列文章

  1. RecyclerView的超强辅助Graywater——理论篇
  2. RecyclerView的超强辅助Graywater——基础实操篇
  3. RecyclerView的超强辅助Graywater——点击事件
  4. RecyclerView的超强辅助Graywater——综合实操篇

原本上一篇写完之后就想接着就把实操篇文章给完成了的,但是没想到新需求一来,就加了2周的班,到今天才空下来,才有时间继续完成这篇文章。

上一篇讲解了Graywater的一些基础理论知识,这一篇文章将讲解Graywater的基本使用方法,并搭配了一个基础Demo。下一篇则会讲解Graywater的进阶用法。

注意

以下所使用的Graywater类库,非Graywater最新类库,比最新的旧一点,但是使用起来没有任何问题,个人认为这版比最新的要好理解很多。Graywater官方的类库已经更新到最新,我使用的这个版本Github地址:GraywaterPrimaryDemo Github地址,大家在项目里面可以找到graywater类库。

使用Graywater的步骤

还是要先祭出这张原理图,里面依然是熟悉的5个类,但是多了一些与步骤相关的文字。在GraywaterAdapter实例化之前,我们需要做一些准备工作,也就是将以下步骤中相关类全部创建完成。这是我在实际使用后,觉得最佳的一个文件创建顺序。


带步骤的原理图.png

基础Demo预览效果

根据上面所说的步骤,我们首先来看看如何使用Graywater做出一个最简单的列表。

Graywater简单列表Demo.gif

先展示一下GraywaterPrimaryDemo项目的目录结构

目录结构.png

接下来开始使用Graywater实现上面GIF图的效果,要想使用Graywater,肯定要先导入类库啦。

准备工作:导入Graywater类库

从GraywaterPrimaryDemo中将graywater文件夹拷贝到根目录下,在settings.gradle文件中添加graywater

include ':app', 'graywater'

然后选择菜单栏 File --> Project Structure,选择app--->选择Denpendencies--->选择Module dependency,选择graywater进行依赖。如下图。

屏幕快照 2018-11-06 上午12.23.24 2.png

成功导入graywater后,我们就可以开始愉快的写代码了。

第一步:新建布局文件adapter_item.xml

布局文件很简单,只有一个ImageView和TextView,一个用来显示图片,一个用来显示文本,就不贴出代码了,大家可以在最后Github GraywaterPrimaryDemo地址里下载完整代码查看。

第二步:创建需要的model类

首先将官方Demo中的Primitive拷贝到model目录下,为什么一定要拷贝这个接口,原因后面再讲解。

因为Demo里面的数据类型只有一种,每个Item就是一串文本,和一个图片url(这里大家最好是用对象来管理)。所以只需要创建一个EntertainPrimitive类来管理所有数据就可以了。同时记得EntertainPrimitive类需要实现Primitive接口。model目录下所有的类都需要实现Primitive接口。

第三步:创建对应的ViewHolder文件

从GraywaterPrimaryDemo的GIF图上可以看到,每一个Item就只有一个ImageView和一个TextView。所以我们需要创建一个ViewHolder来管理这2个控件。首先也是将官方Demo中的PrimitiveViewHolder拷贝到viewholder目录下,然后新建EntertainViewHolder来继承PrimitiveViewHolder,为什么也要拷贝这个的原因也在后面再讲,我们先关注核心步骤。

在EntertainViewHolder中创建一个ImageView和Textview,使用itemView来查找,这跟在RecyclerView.Adapter使用时是一样的。

    private ImageView img;
    private TextView title;

    public EntertainViewHolder(View itemView) {
        super(itemView);
        img = itemView.findViewById(R.id.img);
        title = itemView.findViewById(R.id.name);
    }

    public ImageView getImg() {
        return img;
    }

    public TextView getTitle() {
        return title;
    }

第四步:创建对应的ViewHolderCreator文件

EntertainViewHolder创建好了,接下来就需要新建一个viewholdercreator类来生成viewholder。在viewholdercreator目录下创建EntertainViewHolderCreator类,实现GraywaterAdapter.ViewHolderCreator接口,同时要实现接口中的create方法和getViewType方法。

public class EntertainViewHolderCreator implements GraywaterAdapter.ViewHolderCreator {
    @Override
    public RecyclerView.ViewHolder create(ViewGroup parent) {
        return new EntertainViewHolder(GraywaterAdapter.inflate(parent, R.layout.adapter_item));
    }

    @Override
    public int getViewType() {
        return R.layout.adapter_item;
    }
}

create方法返回创建的viewholder对象。
getViewType方法返回的就是item的layoutId。

EntertainViewHolderCreator就是EntertainViewHolder的制造器,专门负责创建EntertainViewHolder对象。有一个需要注意的地方,在new一个EntertainViewHolder对象时,参数是需要传入itemView的,这里使用的GraywaterAdapter.inflate(),查看源码可知,其实就是对LayoutInflater.from(parent.getContext()).inflate();再封装了一层。

GraywaterAdapter.inflate(parent, R.layout.adapter_item)  //实际上就是调用的LayoutInflater.from(parent.getContext()).inflate();

第五步:创建对应的Binder文件

数据model和视图viewholder都创建好了,接下来就可以使用Binder来绑定数据了。创建EntertainBinder类实现GraywaterAdapter.Binder接口,接口里需要传入model的泛型和viewholder的泛型,因为这里是EntertainPrimitive数据所对应的部分,所以直接传EntertainPrimitive和EntertainViewHolder就好。

实现接口里的方法,主要需要重写下面3个方法:

getViewHolderType() : 返回ViewHolder的类型。
bind() : 将model的数据绑定到view上,这里就类似于RecyclerView.Adapter的onBindViewHolder方法。
unbind() :做一些清理操作,一般在里面解除监听事件。

GraywaterPrimaryDemo里的EntertainBinder类也写的很简单

public class EntertainBinder implements GraywaterAdapter.Binder<EntertainPrimitive, EntertainViewHolder> {
    @NonNull
    @Override
    public Class<EntertainViewHolder> getViewHolderType() {
        return EntertainViewHolder.class;
    }

    @Override
    public void prepare(@NonNull EntertainPrimitive model, List<GraywaterAdapter.Binder<? super EntertainPrimitive, ? extends EntertainViewHolder>> binders, int binderIndex) {

    }

    @Override
    public void bind(@NonNull EntertainPrimitive model, @NonNull EntertainViewHolder holder, @NonNull List<GraywaterAdapter.Binder<? super EntertainPrimitive, ? extends EntertainViewHolder>>
            binders, int binderIndex, @NonNull GraywaterAdapter.ActionListener<EntertainPrimitive, EntertainViewHolder> actionListener) {
        Picasso.get().load(model.getUrls().get(binderIndex)).placeholder(R.mipmap.ic_launcher).into(holder.getImg());
        holder.getTitle().setText(model.getTitles().get(binderIndex));
    }

    @Override
    public void unbind(@NonNull EntertainViewHolder holder) {}

}

第六步:创建对应的ItemBinder文件

基本数据model类,视图类viewholder,视图创建器类viewholdercreator类,数据视图绑定类binder类创建好后,就可以创建最后一个ItemBinder类了,这里我们创建对应的EntertainItemBinder类来对EntertainBinder类进行管理。

EntertainItemBinder类的一个作用就是使用getBinderList()方法返回当前所管理所有Binder对象。

    public List<GraywaterAdapter.Binder<? super EntertainPrimitive, ? extends PrimitiveViewHolder>> getBinderList(@NonNull final EntertainPrimitive model, int position) {
        return new ArrayList<GraywaterAdapter.Binder<? super EntertainPrimitive, ? extends PrimitiveViewHolder>>() {{
            for (String s : model.getTitles()) {
                add(entertainBinder);
            }
        }};
    }

说一下为什么要写for循环,Demo中可以看到,每一个福原爱Item就是一个子数据,对应着一个ViewHolder和一个Binder,所以一个EntertainPrimitive的model里有多少个titles就对应了有多少个子数据,就添加到binderlist中,再一起返回,数据就能显示出来了。

最后一步,创建PrimitiveAdapter

所有准备工作做好了,就可以创建适配器PrimitiveAdapter来继承GraywaterAdapter了,注意这里因为要传入3个泛型参数,分别是model、viewholder和model的class类型

public class PrimitiveAdapter extends GraywaterAdapter<Primitive, PrimitiveViewHolder, Class<? extends Primitive>> {

    public PrimitiveAdapter() {
        register(new EntertainViewHolderCreator(), EntertainViewHolder.class);  //将creator和对应的viewholder绑定

        EntertainBinder entertainBinder = new EntertainBinder();
        EntertainItemBinder entertainItemBinder = new EntertainItemBinder(entertainBinder);
        register(EntertainPrimitive.class, entertainItemBinder, null);  //将itemBinder和指定的数据类型绑定
    }

    @Override
    protected Class<? extends Primitive> getModelType(Primitive model) {
        return model.getClass();
    }
}

可以看到有2个register()方法,一个是绑定viewholder和viewholdercreator的,一个是绑定itemBinder和数据类型的。实际上register()方法就是把对应的数据放入到map集合中,然后迅速查找对应的对象。每个数据类型的register只需要写一次就够了。可以看到第二个register()方法的第三个参数传入了null值,这是因为第三个参数是拿来传入点击事件的listener的。没有点击事件就可以直接传入null,需要点击事件就让ItemBinder类实现GraywaterAdapter.ActionListener接口,再传入register方法中。

疑问解答

前面的部分留下了2个疑问,为什么一定要将Primitive和PrimitiveViewHolder类拷贝到对应目录下,并需要继承。我们可以从上面ItemBinder传入的泛型看到是Primitive和PrimitiveViewHolder。这就是泛型的魅力了,在不同的数据类型的情况下,比如还有SportsPrimitive、NewsPrimitive或SportsViewHolder和NewsViewHolder类,因为都实现了Primitive接口,继承了PrimitiveViewHolder类的原因,就能在PrimitiveAdapter中统一管理。

Primitive接口还有一个作用,就是用来区别不同的数据类型。这个可以在Graywater的官方Demo中看到,接下来的一篇Graywater的进阶使用也会讲到。

显示RecyclerView

到这里,Graywater的部分就创建完了。我们只需要在MainActivity中,将EntertainPrimitive赋值,再传给PrimitiveAdapter就可以使用了。

    private void initData() {
        List<String> urls = new ArrayList<>();
        List<String> titles = new ArrayList<>();

        for (int i = 0; i < 40; i++) {
            urls.add("http://n.sinaimg.cn/ent/4_img/upload/0b3147ad/107/w1024h683/20181023/M34_-hmuuiyw5724330.jpg");
            titles.add("item " + (i + 1) + " : " + "福原爱现身退役发布会挥泪告别 甜美比心畅聊感慨");
        }

        mEntertainPrimitive = new EntertainPrimitive(urls, titles);
    }

    private void initContentView() {
        mRecyclerView = findViewById(R.id.recyclerview);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        mPrimitiveAdapter = new PrimitiveAdapter();
        mRecyclerView.setAdapter(mPrimitiveAdapter);
    }

    private void updateRecyclerView() {
        mPrimitiveAdapter.add(mEntertainPrimitive);
        mPrimitiveAdapter.notifyDataSetChanged();
    }

总结

按照步骤一步步下来,创建Graywater的过程还是很清晰的。大家有什么疑问可以在评论区提问。

GraywaterPrimaryDemo Github地址
如果对大家有帮助的话,希望大家点下star,谢谢,

如果对你有帮助的话,点赞、评论、赞赏都是对我的鼓励,也是支持我写下去的动力,谢谢!

推荐阅读更多精彩内容