RecyclerView的最佳使用方案
RecyclerView已经广为人知,并且别大家所使用。然而在使用的过程中,有一些却不是RecyclerView开发者希望使用的
ViewHolder
你怎么使用你的ViewHolder?
//定义
class ViewHolder {
TextView title;
TextView body;
ImageView icon;
}
//adapter中使用
public void onBindViewHolder(ViewHolder vh, int pos)
{
Item item = items.get(pos);
title.setText(item.getTitle());
body.setText(item.getBody());
imageLoader.loadImage(icon, item.IconUrl());
}
不知道你是不是这么写的,反正我看到的代码,99%都是这么撸出来的。
在这种写法中,我们需要获取到ViewHolder,然后获取到Item数据,然后将二者进行绑定。但是这二者其实是没有任何关系的。
最佳写作方法是啥呢?这可不是我说的,是Yigit这么说的,别赖我。
//定义
class ViewHolder {
...
public bindTo(Item item, ImageLoader imageLoader) {
title.setText(item.getTitle());
body.setText(item.getBody());
imageLoader.loadImage(icon, item.IconUrl());
}
}
//adatper中调用
public void onBindViewHolder(ViewHolder vh, int position) {
vh.bindTo(items.get(position), mImageLoader);
}
在这种写法中,我们在ViewHolder中进行View的定义,并且生成一个绑定方法bindTo(),通过这个方法,我们知道,我们显示这个ViewHolder,需要一个Item类以及一个ImageLoader,至于这两个参数如何和ViewHolder进行数据的关联,交给ViewHolder进行处理。
而且,这样你的ViewHolder也可以在应用的其他地方进行使用,就像一个Presenter一样。你的ViewHolder内部进行了很好的封装。
View Types
在我们进行多条目的显示时,一般我们都会使用到ViewTypes。但是如何才能进行更好的代码书写呢?
你是如何做的呢?
@Override
public int getItemViewType(int position) {
User user = mItems.get(position);
if (user.isPremium()) {
return TYPE_PREMIUM;
}
return TYPE_BASIC;
}
然后再根据不同的类型,绘制不同的布局,生成ViewHolder。
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case TYPE_PREMIUM:
view = mLayoutInflater.inflate(R.layout.premium, parent,false);
break;
case TYPE_BASIC:
view = mLayoutInflater.inflate(R.layout.basic, parent,false);
break;
}
return new UserViewHolder(view);
}
如果只有两种类型,很OK,但是如果有很多种类型呢?我们就需要在getItemViewType中的类型和onCreateViewHolder中的类型进行好好的对应了。一个不小心,就over叻。
其实,我们可以耍个小滑头。
@Override
public int getItemViewType(int position) {
User user = mItems.get(position);
if (user.isPremium()) {
return R.layout.premium;
}
return R.layout.basic;
}
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
view = mLayoutInflater.inflate(viewType, parent,false);
return new UserViewHolder(view);
}
我们可以将布局文件的id进行返回,我们知道,布局文件的id是系统生成的,而且是唯一的。一旦你这么做了,我们看到,在onCreateViewHolder中,我们不需要再重复处理复杂的逻辑。
OnClickListener
众所周知,RecyclerView中,去掉了setOnItemClicker方法,而我们一般通过自己定义一个点击事件的回调进行处理。
public void onBindViewHolder(ViewHolder vh,final int position) {
vh.likeButton.setOnClickListener = new OnClickListener() {
items[position].liked = true;
notifyItemChanged(position);
}
}
这块代码段估计是我们经常写的代码了,其实它是存在问题的。
1.当我们每次调用数据和viewholder进行绑定的时候,都重新生成了一个OnClickListener()。
2.在ListView中,我们这样使用postion是没有任何问题的,但是在Recyclerview中,postion并不是final类型,它是可能会该病的。
谷歌更加推荐我们在ViewHolder生成的时候进行点击事件的绑定。代码如下:
public onCreateViewHolder(...) {
final ViewHolder vh = ....;
myViewHolder.itemView.setOnClickListener({
int pos = vh.getAdapterPosition();
if (pos != NO_POSITION) {
items[position].liked = true;
notifyItemChanged(position);
}
});
}
}
其中,getAdapterPosition()这个方法是谷歌最推行的获取ViewHolder在adapter中的位置的方法。
这个方法中,还需要注意的一点,是NO_POSITION(-1)的判断,由于RecyclerView是异步,如果我们将所有的items都删除了,而Recyclerview没有重新进行绘制,那么当我们再次点击条目的时候,获取到的postion将会是-1。
先记录这么些吧。关于Recyclerview的一些官方推荐写法和介绍,以后慢慢记录。