RecyclerView的超强辅助Graywater——基础实操
关于Graywater的系列文章
原本上一篇写完之后就想接着就把实操篇文章给完成了的,但是没想到新需求一来,就加了2周的班,到今天才空下来,才有时间继续完成这篇文章。
上一篇讲解了Graywater的一些基础理论知识,这一篇文章将讲解Graywater的基本使用方法,并搭配了一个基础Demo。下一篇则会讲解Graywater的进阶用法。
注意
以下所使用的Graywater类库,非Graywater最新类库,比最新的旧一点,但是使用起来没有任何问题,个人认为这版比最新的要好理解很多。Graywater官方的类库已经更新到最新,我使用的这个版本Github地址:GraywaterPrimaryDemo Github地址,大家在项目里面可以找到graywater类库。
使用Graywater的步骤
还是要先祭出这张原理图,里面依然是熟悉的5个类,但是多了一些与步骤相关的文字。在GraywaterAdapter实例化之前,我们需要做一些准备工作,也就是将以下步骤中相关类全部创建完成。这是我在实际使用后,觉得最佳的一个文件创建顺序。
带步骤的原理图.png
基础Demo预览效果
根据上面所说的步骤,我们首先来看看如何使用Graywater做出一个最简单的列表。
Graywater简单列表Demo.gif先展示一下GraywaterPrimaryDemo项目的目录结构
接下来开始使用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,谢谢,
如果对你有帮助的话,点赞、评论、赞赏都是对我的鼓励,也是支持我写下去的动力,谢谢!