最近在学习Material Design设计,首当其冲就是学习RecyclerView,参考了下Hongyang自己对RecyclerView的ViewHolder与Adapter做了下简单的封装。下面,废话不多说,直接上干货。
ViewHolder在RecyclerView中的作用主要就是用来缓存我们每一个Item对应的View视图的,我们在Adapter中主要是在public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)回去创建一个ViewHolder,在 public void onBindViewHolder(ViewHolder holder, int position)会将holder与该position处的数据进行绑定(就是设置数据该怎么显示,这里需要访问ViewHolder中缓存的View)。基于此我们可以总结出ViewHolder的基本功能:
1 提供创建ViewHolder的功能
这里主要包括两种,一种是直接传入一个View参数创建一个View,比如我们在ListView中经常使用的下面代码:
View view = LayoutInflater.from(mContext).inflate(R.layout.xxx,parent,false);
ViewHolder holder = new ViewHolder(view);
第二种就是直接传入一个layoutId,这一种我们在多种ItemType类型中经常会用到。
2 对ViewHolder中各种View的访问
一般ViewHolder中会有TextView,ImageView等,我们通过使用一个SparseArray来缓存我们加载过的View,我们经常需要对它设置文本内容,字体大小,图片内容等。
3 封装ViewHolder中各种View事件
一般主要是点击事件与长按事件,我这里只封装了点击事件
下面直接上封装好的ViewHolder
/**
* Created by qiyei2015 on 2016/11/28.
* [email protected]
*/
public class ViewHolder extends RecyclerView.ViewHolder {
private Context mContext;
private SparseArray mViews;//存储View,key 为int,value为View类型
private View mConvertView; //存储加载布局xml中的的所有View
public ViewHolder(Context context,View view){
super(view);
mContext = context;
mConvertView = view;
mViews = new SparseArray<>();
}
/**
* 创建ViewHolder,外部调用
* @param context
* @param view
* @return
*/
public static ViewHolder createViewHolder(Context context,View view){
return new ViewHolder(context,view);
}
/**
* 创建ViewHolder
* @param context
* @param parent
* @param layoutId
* @return
*/
public static ViewHolder createViewHolder(Context context, ViewGroup parent,int layoutId){
View itemView = LayoutInflater.from(context).inflate(layoutId,parent,false);
return new ViewHolder(context,itemView);
}
/**
* 根据id来查找View
* @param viewId
* @param
* @return
*/
public T getView(int viewId){
View view = mViews.get(viewId);
if (view == null){ //如果缓存里面没有,就从布局文件中查找
view = mConvertView.findViewById(viewId); //从xml查找的,减少重复调用findViewById
mViews.put(viewId,view);
}
return (T) view;
}
/**
* 获取ConvertView
* @return
*/
public View getConvertView(){
return mConvertView;
}
/**
* 以下是设置TextView,imageView等View的一些常用方法
* */
/**
* 设置Text
* @param viewId
* @param text
* @return
*/
public ViewHolder setText(int viewId,CharSequence text){
TextView view = getView(viewId);
view.setText(text);
return this;
}
/**
* 设置ImageView
* @param viewId
* @param resId
* @return
*/
public ViewHolder setImageResource(int viewId,int resId){
ImageView view = getView(viewId);
view.setImageResource(resId);
return this;
}
/**
* 设置ImageView的bitmap
* @param viewId
* @param bitmap
* @return
*/
public ViewHolder setImageBitmap(int viewId, Bitmap bitmap){
ImageView view = getView(viewId);
view.setImageBitmap(bitmap);
return this;
}
/**
* 设置View的点击事件
* @param viewId
* @param listener
* @return
*/
public ViewHolder setOnClickListener(int viewId,View.OnClickListener listener){
View view = getView(viewId);
view.setOnClickListener(listener);
return this;
}
}
这个就是我们封装好的ViewHolder,比较简单,使用起来还是比较方便的。使用静态方法创建对象。使用共有方法去设置对象,需要传入一个View的id值即可,可以根据项目的需要慢慢增加功能。
CommonAdapter的封装就比较简单,主要是复写public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)和
public void onBindViewHolder(ViewHolder holder, int position)。其中有一个抽象的方法public abstract void convert(ViewHolder holder,T t,int position);这个主要是把holder与position处的数据做绑定的,这个需要子类来覆写,由子类来实现。
/**
* Created by qiyei2015 on 2016/11/30.
* 1273482124@qq.com
*/
public abstract class CommonAdapter<T> extends RecyclerView.Adapter<ViewHolder> {
protected Context mContext;
protected List mDatas;
protected LayoutInflater mInflater;
protected int mLayoutId;
public CommonAdapter(Context context,List list,int layoutId){
mContext = context;
mDatas = list;
mInflater = LayoutInflater.from(mContext);
mLayoutId = layoutId;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ViewHolder holder = ViewHolder.createViewHolder(mContext,parent,mLayoutId);
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
convert(holder,mDatas.get(position),position);
}
@Override
public int getItemCount() {
return mDatas.size();
}
/**
* 设置数据
* @param data
*/
public void setData(List data){
mDatas = data;
notifyDataSetChanged();
}
/**
* 根据position返回对应的数据
* @param position
* @return
*/
public T getItem(int position){
return mDatas.get(position);
}
/**
* 子类实现的转换方法
*/
public abstract void convert(ViewHolder holder,T t,int position);
}
这样子类只需要实现convert(ViewHolder holder,T t,int position)就可以了。比如:
CommonAdapter adapter = new CommonAdapter() {
@Override
public void convert(ViewHolder holder, String s, int position) {
holder.setText(R.id.tv,"hello");
}
};
在项目中,我们经常需要支持多种类型的Item的Adapter,例如今日头条的界面:
基本思想就是复写public int getItemViewType(int position),然后根据position处返回特定的type,然后在根据type类型返回响应的布局id,根据布局id生成响应的View,这样就可以实现如上图的效果了。
这里我们引入另外一个接口来支持多种类型的Item。
public interface MultiTypeItem {
//根据itemType获取布局文件
int getLayoutId(int itemType);
//根据position及T 数据类型 返回对应的ItemType
int getItemType(int position,T t);
}
支持多种ItemType的Adapter如下:
/**
* Created by qiyei2015 on 2016/12/10.
* 1273482124@qq.com
*/
public abstract class MultiAdapter<T> extends CommonAdapter<T> {
protected MultiTypeItem mMultiItemType;
public MultiAdapter(Context context, List datas, MultiTypeItem itemType){
super(context,datas,-1);
mMultiItemType = itemType;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int id = mMultiItemType.getLayoutId(viewType);
return ViewHolder.createViewHolder(mContext,parent,id);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
}
/**
* 返回position 对应的type
* @param position
* @return
*/
@Override
public int getItemViewType(int position) {
return mMultiItemType.getItemType(position,getItem(position));
}
}
使用也很简单,首先新建一个MultiTypeItem对象。然后新建MultiAdapter实例即可。