一直都想写一个通用的RecyclerView适配器,但是一直都无从下手,后来看了鸿洋大神的博客后才知道怎么写,并且在此基础上添加了点自己的东西,终于算是大功告成。先上代码看看

public class ViewHolder extends RecyclerView.ViewHolder {

    private Context context;
    private SparseArray<View> mViews;
    private View mConvertView;

    public ViewHolder(Context context, View itemView, ViewGroup parent) {
        super(itemView);
        this.context=context;
        this.mConvertView=itemView;
        mViews=new SparseArray<>();
    }

    //获得holder对象
    public static ViewHolder getHolder(Context context,ViewGroup parent,int layoutId){
        View itemView= LayoutInflater.from(context).inflate(layoutId,parent,false);
        ViewHolder holder=new ViewHolder(context,itemView,parent);
        return holder;
    }

    //通过id获取控件
    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;
    }

    /******************************以下是辅助方法******************************/

    //设置文字的方法
    public ViewHolder setTexts(int viewId,String text){
        TextView tv=getView(viewId);
        tv.setText(text);
        return this;
    }

    //设置图片的方法
    public ViewHolder setImage(int viewId,int resId){
        ImageView iv=getView(viewId);
        iv.setImageResource(resId);
        return this;
    }

    //设置点击事件
    public ViewHolder setClickListener(int viewId, View.OnClickListener listener){
        View view=getView(viewId);
        view.setOnClickListener(listener);
        return this;
    }

}

这是一个通用Viewholder,大家可以看到代码很简单,包括构造函数总共也就三个函数而已其他两个分别是getHolder(获得holder对象的方法)和getView(通过id获得view的方法),在这个通用holder中定义了三个变量,其中SparseArray是用来存放View的,这个东西可有可无,用了比不用更有效率,不用的话这样写就好了(getView方法中):View view=mConvertView.findViewById(viewId); return (T)view;,其他两个也没什么异议的了,相信大家都懂的。以上这些就是通用ViewHolder主要的部分了。至于后面的三个辅助方法则是为了更方便的设置图片文字等,大家有需要的可以自行加上去即可。接下来我们说说getView方法,这个方法就是我们找到item里的控件的方法,大家可以看到,我们只需要将控件的id传入即可。看的不是很明白的同学可以点开我上面链接的鸿洋大神的博客来看。
ViewHolder先到这里,现在看看RecyclerView的Adapter。

public abstract class CommentAdapter<T> extends RecyclerView.Adapter<ViewHolder> {

    protected Context mContext;
    protected int mLayoutId;
    protected List<T> mDatas;
    protected LayoutInflater mInflater;

    /**********设置FooterView和HeaderView********************/
    private static final int TYPE_NORMAL = 0;
    private static final int TYPE_FOOTER = 1;
    private static final int TYPE_HEADER = 2;

    private int headerViewId = 0;
    private int footerViewId = 0;

    public void setFooterView(int footerViewId) {
        this.footerViewId = footerViewId;
        notifyItemInserted(getItemCount() - 1);
    }

    public int getFooterView() {
        return footerViewId;
    }

    public void setHeaderView(int headerViewId) {
        this.headerViewId = headerViewId;
        notifyItemInserted(0);
    }

    public int getHeaderViewId() {
        return headerViewId;
    }

    /**********************  Item点击接口  *********************/
    public interface OnItemClickListener {
        void onItemClick(View view, int position);
    }

    private OnItemClickListener onItemClickListener;

    //设置item点击时间监听的方法
    public void setOnItemClickLitener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    /************************构造器****************************/

    public CommentAdapter(Context context, int layoutId, List<T> datas) {
        mContext = context;
        mInflater = LayoutInflater.from(context);
        mLayoutId = layoutId;
        mDatas = datas;
    }

    /***************加载更多数据方法**************/

    public void onLoadMoreDatas(List<T> datas) {
        if (mDatas == null) {
            mDatas = datas;
            notifyDataSetChanged();
        } else {
            mDatas.addAll(datas);
            notifyDataSetChanged();
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        if (headerViewId != 0 && viewType == TYPE_HEADER) {    
            return ViewHolder.getHolder(mContext, parent, headerViewId);
        } else if (footerViewId != 0 && viewType == TYPE_FOOTER) {    //假如footerViewId不等于0和viewType等于TYPE_FOOTER则返回footerView
            return ViewHolder.getHolder(mContext, parent, footerViewId);
        }
        ViewHolder viewHolder = ViewHolder.getHolder(mContext, parent, mLayoutId);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        final int pos = holder.getLayoutPosition();//获得item的位置
        if (getItemViewType(pos) == TYPE_HEADER) {
            convert(holder, null, position, false, true);
        } else if (getItemViewType(pos) == TYPE_FOOTER) {
            convert(holder, null, position, true, false);
        } else {
            if (getHeaderViewId() != 0) {
                convert(holder, mDatas.get(position - 1), position, false, false);
            } else {
                convert(holder, mDatas.get(position), position, false, false);
            }

        }

        if (onItemClickListener != null) {
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onItemClickListener.onItemClick(holder.itemView, pos);
                }
            });
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (footerViewId == 0 && headerViewId == 0) return TYPE_NORMAL;
        if (headerViewId != 0) {
            if (position == 0) {
                return TYPE_HEADER;
            }
        }
        if (footerViewId != 0) {
            if (position == getItemCount() - 1)
                return TYPE_FOOTER;
        }
        return TYPE_NORMAL;
    }

    @Override
    public int getItemCount() {
        if (footerViewId != 0 && headerViewId != 0) {
            return mDatas.size() + 2;
        } else if (footerViewId != 0 && headerViewId == 0) {
            return mDatas.size() + 1;
        } else if (footerViewId == 0 && headerViewId != 0) {
            return mDatas.size() + 1;
        }
        return mDatas.size();
    }

    public abstract void convert(ViewHolder holder, T t, int position, boolean isFooter, boolean isHeader);
}

大家可以看到,我这个通用Adapter中提供了插入Header和Footer的方法和Item点击的方法,插入Footer和Header方法也只需要提供布局id即可,关于这个插入footer和header,最主要的是判断返回item数目,相信大家都可以看的懂的。其他的我就不说那么多了,主要就是通过泛型接收数据并设置上去,加载更多数据方法也是,接收要添加的数据并通知更新即可,在这里有个点要提醒大家,有人说Recyclerview使用notifyDataSetChanged()不如直接使用Listview,对此我并不是很懂是为什么,反正我只会用notifyDataSetChanged()来通知更新批量数据。还有很多不完善的地方,我这里只是提供个例子供大家参考,不喜勿喷。最后贴一下调用适配器的例子,

public class Test extends Activity {
//这里是找id,没用过这个框架的同学用传统的方法即可
    @BindView(R.id.rv_test)
    RecyclerView mTest;
    @BindView(R.id.bt_addHeader)
    Button mAddHeader;
    @BindView(R.id.bt_addFooter)
    Button mAddFooter;
    @BindView(R.id.bt_addDatas)
    Button mAddDatas;

    TextView loadMore;
    ProgressBar pb;
    TextView loadMore2;
    ProgressBar pb2;

    CommentAdapter<String> commentAdatper = null;
    private List<String> datas = new ArrayList<>();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        View view = View.inflate(Test.this, R.layout.activity_mvc, null);
        ButterKnife.bind(Test.this, view);
        setContentView(view);

        init();

    }


    private void init() {
        addDatas();
        initListener();
        mTest.setLayoutManager(new LinearLayoutManager(this));
        mTest.setAdapter(commentAdatper = new CommentAdapter<String>(Test.this, R.layout.item_test, datas) {
            @Override
            public void convert(final ViewHolder holder, final String s, final int position, boolean isFooter, boolean isHeader) {
                if (isHeader) {
                    loadMore2 = holder.getView(R.id.tv_loadMore);
                    pb2 = holder.getView(R.id.pb_loading);
                } else if (isFooter && position == commentAdatper.getItemCount() - 1) {
                    loadMore = holder.getView(R.id.tv_loadMore);
                    pb = holder.getView(R.id.pb_loading);
                } else {
                    holder.setTexts(R.id.tv_test, position + s);
                }

            }
        });
        mTest.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
        commentAdatper.setOnItemClickLitener(new CommentAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(Test.this, "点击了" + position + "项", Toast.LENGTH_SHORT).show();
                if (commentAdatper.getHeaderViewId() != 0 && position == 0) {
                    loadMore2.setVisibility(View.GONE);
                    pb2.setVisibility(View.VISIBLE);
                }
                if (commentAdatper.getFooterView() != 0 && position == commentAdatper.getItemCount() - 1) {
                    loadMore.setVisibility(View.GONE);
                    pb.setVisibility(View.VISIBLE);
                }
            }
        });


    }

    private void initListener() {
        mAddHeader.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                commentAdatper.setHeaderView(R.layout.item_footerview);
            }
        });
        mAddFooter.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                commentAdatper.setFooterView(R.layout.item_footerview);
            }
        });
        mAddDatas.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                commentAdatper.onRefreshDatas(newDatas());
            }
        });
    }

    private void addDatas() {
        datas.add("123");
        datas.add("是领导看看菲拉斯");
        datas.add("我我我");
        datas.add("lsjd");
        datas.add("sdf");
        datas.add("fgj");
        datas.add("rty");
        datas.add("jh,");
        datas.add("123");
        datas.add("是领导看看菲拉斯");
        datas.add("我我我");
        datas.add("lsjd");
        datas.add("sdf");
        datas.add("fgj");
        datas.add("rty");
        datas.add("jh,");
        datas.add("123");

    }

    private List<String> newDatas() {
        List<String> datas = new ArrayList<>();
        datas.add("东东");
        datas.add("南南");
        datas.add("西西");
        datas.add("北北");
        return datas;
    }

}

最后是对应的布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
        <Button
            android:id="@+id/bt_addHeader"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="添加Header"
            />
        <Button
            android:id="@+id/bt_addFooter"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:text="添加Footer"
            />

        <Button
            android:id="@+id/bt_addDatas"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:text="添加Datas"
            />

    </LinearLayout>


    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_test"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

    </android.support.v7.widget.RecyclerView>

</LinearLayout>

item的布局文件大家可以自行写,这里就不贴了。我写的通用适配器有很多不完善的地方,希望能给大家提供个参考,不懂的地方可以在下面留言,我一定全力解答。我旨在提供个例子,让有需要的人参考。希望大家能够共同进步,共同学习,祝早日走上人生巅峰。