在项目中大量使用了RecyclerView控件来展示复杂的列表,关于RecyclerView写的最多的就是Adapter了。
对于Adapter一般都继承RecyclerView.Adapter复写几个方法,写下来发现大部分的代码基本都是类似的。
本篇开始一步一步封装出一个通用的Adapter。
本篇是对单类型Item的Adapter的封装
RecyclerView 的Adapter分析
要想分装出一个通用的Adapter,首先需要对Adapter中的代码进行分析,再试着进行抽取
public class GankAdapter extends RecyclerView.Adapter {
private Context mContext;
private List mGankBeanList;
private LayoutInflater mLayoutInflater;
public GankAdapter(Context context, List gankBeanList) {
this.mContext = context;
this.mGankBeanList = gankBeanList;
mLayoutInflater = LayoutInflater.from(mContext);
}
public void addData(int position, List data) {
if (data != null && data.size() > 0) {
mGankBeanList.addAll(position, data);
notifyItemRangeInserted(position, data.size());
}
}
public void setData(List list) {
if (list != null) {
mGankBeanList = list;
notifyDataSetChanged();
}
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View contentView = mLayoutInflater.inflate(R.layout.item_list, parent, false);
return new MyViewHolder(contentView);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
GankBean gankBean = mGankBeanList.get(position);
holder.tvTitle.setText(gankBean.getDesc());
holder.tvUrl.setText(gankBean.getUrl());
}
@Override
public int getItemCount() {
return mGankBeanList == null ? 0 : mGankBeanList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
public TextView tvTitle;
public TextView tvUrl;
public MyViewHolder(View itemView) {
super(itemView);
tvTitle = (TextView) itemView.findViewById(R.id.tv_title);
tvUrl = (TextView) itemView.findViewById(R.id.tv_url);
}
}
}
继承与RecyclerView.Adapter一般需要重写以下方法:
1、构造函数,传入数据和Context对象
2、getItemCount() 返回Item 的个数
3、getItemType()返回Item 的类型(本篇只做单类型的封装下篇在讲)
4、onCreateViewHolder()创建ViewHolder
5、onBindViewHolder()绑定ViewHolder的数据
其中有一个很关键的角色 就是ViewHolder,ViewHolder 的作用是什么呢?
ViewHolder的主要的作用是存储对应的convertView中需要操作的子View,避免每次findViewById,从而提升运行的效率;
ViewHolder 的封装
经过以上分析,ViewHolder 的作用是存储需要操作的子View,对于不同的ViewHolder我们不知道需要存储的View 是什么类型,有多少数量
所以只能通过集合的方式来存储子View,存储的容器采用SparseArray容器,相当于一个Hash
而关于子View,我们知道的恰恰是id,为int型,刚好符合了需求。
public class ViewHolder extends RecyclerView.ViewHolder
{
private SparseArray mViews; //存储子View
private View mConvertView;
private Context mContext;
//构造函数 初始化容器和contvertView
public ViewHolder(Context context, View itemView){
super(itemView);
mContext = context;
mConvertView = itemView;
mViews = new SparseArray<>();
}
//根据ItemView 创建ViewHolder
public static ViewHolder createViewHolder(Context context, View itemView {
return new ViewHolder(context, itemView);
}
//根据layoutId 创建ViewHolder
public static ViewHolder createViewHolder(Context context,
ViewGroup parent, int layoutId) {
View itemView = LayoutInflater.from(context).inflate(layoutId, parent,
false);
return new ViewHolder(context, itemView);
}
/**
* 通过viewId获取控件
*
* @param viewId
* @return
*/
public T getView(int viewId)
{
//查找集合中是否存在子View
View view = mViews.get(viewId);
//不存在,执行findViewById操作
if (view == null)
{
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
public View getConvertView()
{
return mConvertView;
}
/****以下为辅助方法,可以快速的使用*****/
public ViewHolder setText(int viewId, String text)
{
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
public ViewHolder setImageResource(int viewId, int resId)
{
ImageView view = getView(viewId);
view.setImageResource(resId);
return this;
}
public ViewHolder setImageBitmap(int viewId, Bitmap bitmap)
{
ImageView view = getView(viewId);
view.setImageBitmap(bitmap);
return this;
}
public ViewHolder setImageDrawable(int viewId, Drawable drawable)
{
ImageView view = getView(viewId);
view.setImageDrawable(drawable);
return this;
}
/**
* 为ImageView设置网络url 或者 文件path 图片
* @param viewId View的id
* @param path url或者 文件path
* @param errorId 错误图片id
* @param placeId 占位图片id
* @return
*/
public ViewHolder setImageBUrlOrFile(int viewId,String path,int errorId,int placeId){
ImageView imageView = getView(viewId);
MyImageLoader.getInstance().displayImage(mContext,path,imageView,errorId,placeId);
return this;
}
public ViewHolder setImageNoPlace(int viewId,String path){
ImageView imageView = getView(viewId);
MyImageLoader.getInstance().displayImage(mContext,path,imageView);
return this;
}
public ViewHolder setBackgroundColor(int viewId, int color)
{
View view = getView(viewId);
view.setBackgroundColor(color);
return this;
}
public ViewHolder setBackgroundRes(int viewId, int backgroundRes)
{
View view = getView(viewId);
view.setBackgroundResource(backgroundRes);
return this;
}
public ViewHolder setTextColor(int viewId, int textColor)
{
TextView view = getView(viewId);
view.setTextColor(textColor);
return this;
}
public ViewHolder setTextColorRes(int viewId, int textColorRes)
{
TextView view = getView(viewId);
view.setTextColor(mContext.getResources().getColor(textColorRes));
return this;
}
public ViewHolder setVisible(int viewId, boolean visible)
{
View view = getView(viewId);
view.setVisibility(visible ? View.VISIBLE : View.GONE);
return this;
}
public ViewHolder setTag(int viewId, Object tag)
{
View view = getView(viewId);
view.setTag(tag);
return this;
}
public ViewHolder setTag(int viewId, int key, Object tag)
{
View view = getView(viewId);
view.setTag(key, tag);
return this;
}
public ViewHolder setChecked(int viewId, boolean checked)
{
Checkable view = (Checkable) getView(viewId);
view.setChecked(checked);
return this;
}
/****关于点击事件和长按事件*****/
public ViewHolder setOnClickListener(int viewId,
View.OnClickListener listener)
{
View view = getView(viewId);
view.setOnClickListener(listener);
return this;
}
public ViewHolder setOnLongClickListener(int viewId,
View.OnLongClickListener listener)
{
View view = getView(viewId);
view.setOnLongClickListener(listener);
return this;
}
}
通过createViewHolder方法来实例化ViewHolder 对象,实现了Context 、ConvertView、SparseArray的初始化
在需要操作子View 的时候,通过getView 方法来实例化子View对象
在ViewHolder中还封装了一些常用控件的操作方法,注意到 很多方法返回的是ViewHolder对象,使得代码能够链式调用,类似于Builder模式的代码
使用起来如下:
holder.setText(R.id.tv_title,"Title")
.setText(R.id.tv_content,"Content");
Adapter 的封装
有了ViewHolder 的封装,回顾需要重写的几个方法,接下来对Adapter进行封装
public abstract class SingleAdapter extends RecyclerView.Adapter{
protected Context mContext;
protected int mLayoutId;
protected List mDatas;
public SingleAdapter(Context context, int layoutId, List datas){
mContext = context;
mLayoutId = layoutId;
mDatas = datas;
}
@Override
public int getItemCount(){
return mDatas==null?0:mDatas.size();
}
@Override
public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType){
//创建ViewHolder对象
return ViewHolder.get(mContext, parent, mLayoutId);;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position){
//绑定数据交由子类来完成
convert(holder, mDatas.get(position));
}
public abstract void convert(ViewHolder holder, T t);
//以下是一些公共方法的封装
public void clearData() {
mDatas.clear();
notifyItemRangeRemoved(0, mDatas.size());
}
public void removeData(int position) {
mDatas.remove(position);
notifyItemRemoved(position);
if (position != mDatas.size())
notifyItemRangeChanged(position,mDatas.size() - position);
}
public void addData(List list) {
addData(0, list);
}
public void addData(int position, List data) {
if (data != null && data.size() > 0) {
mDatas.addAll(position, data);
notifyItemRangeInserted(position, data.size());
}
}
public void setData(List data) {
if (list != null) {
mDatas = data;
notifyDataSetChanged();
}
}
}
通过构造函数 初始化Conetext ,Data ,layoutId属性
getItemCount()方法返回Data 的数量,并进行了判空
onCreateViewHolder()方法通过ViewHolder 的静态方法初始化ViewHolder
onBindViewHolder()绑定数据是子类来完成的,所以抽象出convert(ViewHolder holder, T t)方法传递参数
子类通过该方法完成数据的绑定
使用
SingleAdapter mSingleAdapter = new SingleAdapter
@Override
public void convert(ViewHolder holder, String s){
holder.setText(R.id.tv_title,s);
}
}