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