一:背景
在最开始学习Android的时候,大部人用ListView都会用到ViewHolder,而对于RecyclerView则是强制使用ViewHolder的,但是很多新手(包括我)一开始都会一个RecyclerView使用一个ViewHolder,这样就会导致ViewHolder复用性太差,而且如果当item布局改变时,ViewHolder也会需要去调整,这对于大型的开发项目显然是十分不利的,所以有必要去封装一个通用的ViewHolder。
比如像如下的代码,当在item布局中去添加一个新的TextView时,就需要去手动的添加。
public static class ViewHolder extends RecyclerView.ViewHolder {
@Bind(R.id.recipe_image)
ImageView image;
@Bind(R.id.recipe_tag)
TextView tag;
@Bind(R.id.recipe_name)
TextView name;
@Bind(R.id.recipe_food)
TextView food;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
二:封装ViewHolder
在RecyclerView中ViewHolder是作为泛型传入的,因为是通用的ViewHolder,所以ViewHolder的内部的控件不能写死,在此我们采用SparseArray来缓存布局中的Views,当getView时,如果SparseArray存储了View的实例,那么直接从SparseArray获取,如果没有存储,那么从itemView上findViewById来查找。除此之外还提供了根据itemView的id获取ViewHolder方法getViewHolder()。
即BaseViewHolder的代码如下
public class ViewHolder extends RecyclerView.ViewHolder
{
private SparseArray<View> mViews;
private View mConvertView;
private Context mContext;
//1 ViewHolder的构造方法
public ViewHolder(Context context, View itemView, ViewGroup parent)
{
super(itemView);
mContext = context;
mConvertView = itemView;
mViews = new SparseArray<View>();
}
//2 通过item_layout的id来构造ViewHolder
public static ViewHolder get(Context context, ViewGroup parent, int layoutId)
{
View itemView = LayoutInflater.from(context).inflate(layoutId, parent,
false);
ViewHolder holder = new ViewHolder(context, itemView, parent, position);
return holder;
}
// 从缓存中获取View控件
public <T extends View> T getView(int viewId)
{
View view = mViews.get(viewId);
if (view == null)
{
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
}
三: 通用RecyclerView.Adapter
前面已经封装好了通用的ViewHolder,RecyclerView.Adapter的两个主要方法是onCreateViewHolder()和onBindViewHolder(),onCreateViewHolder可以根据ViewHolder.getViewHplder生成ViewHolder,onBindViewHolder()主要用来绑定事件和数据,抽象出一个convert方法来完成事件的绑定。
public abstract class CommonAdapter<T> extends RecyclerView.Adapter<ViewHolder>
{
protected Context mContext;
protected int mLayoutId;
protected List<T> mDatas;
protected LayoutInflater mInflater;
public CommonAdapter(Context context, int layoutId, List<T> datas)
{
mContext = context;
mInflater = LayoutInflater.from(context);
mLayoutId = layoutId;
mDatas = datas;
}
@Override
public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType)
{
ViewHolder viewHolder = ViewHolder.get(mContext, parent, mLayoutId);
return viewHolder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position)
{
holder.updatePosition(position);
convert(holder, mDatas.get(position));
}
public abstract void convert(ViewHolder holder, T t);
@Override
public int getItemCount()
{
return mDatas.size();
}
}
使用:实现convert方法,并在convert方法中完成事件的绑定即可。
如果这个时候,如果要在item布局中去添加一个新的TextView时,只需要在绑定的时候去绑定就可以,而不需要像未封装时去修改ViewHolder代码,然后再去绑定
mRecyclerView.setAdapter(new CommonAdapter<String>(this, R.layout.item_list, mDatas)
{
@Override
public void convert(ViewHolder holder, String s)
{
TextView tv = holder.getView(R.id.id_item_list_title);
tv.setText(s);
}
});