RecyclerView万能适配器加强版———可选择添加头和尾(含点击事件)
2016-09-21 03:46
507 查看
最近项目中要实现RecyclerView添加头尾,后在度娘的帮助下完成任务;就在自己为此而窃喜的时候,一个念头飞入我的脑海:将添加头尾的逻辑封装到RecyclerView的万能适配器中,实现可以选择添加。真是飞来横祸啊,我最怕这些想法了,一旦心里这样想了,那便是种下了一颗邪恶的种子,让我心里直痒痒,结果在本能的作用下:开始了一个不眠之夜……
罪过啊,罪过……阿弥陀佛!….
废话少说,下面我们来谈正事:由于本人水平有限,希望各位大神不吝赐教,不甚感激,正所谓:“人过留名,雁过留声。”也希望各位走过路过没有错过的朋友,在评论区写下自己宝贵的想法,鞭策一下我这匹未来的千里马….谢谢;
本次封装实现了:
1. 加载各种Item布局,并做了缓存(在万能适配器的基础上封装的);
2. 选择添加头或尾(都不加,都加,二选一 均可);
3. item的点击事件;
感觉适配器的构造方法不是很理想:头尾二选一的时候必须保留适配器构造方法的三个泛型 , 并且不能设置null(不用的泛型可设置为String不影响) , 要把不选的那个资源文件设置为空,数据源设置为null,希望有识之士提出更好的写法;
让我很纠结的一点是:要不要将头尾的数据源传递到适配器中:传的话要将头尾的数据源封装成类(JavaBean)并在适配器的构造方法中作为泛型导入,比较麻烦,在通常的情况下可以直接在Activity中将数据设置到控件中,但是考虑到List等一些复杂的情况,我还是觉得该予以保留……你的看法呢?期待你的回复….
————-实现逻辑以及主要方法摘要:————————–
1 . 多布局,根据头尾的个数(目前只支持0或1),对每个Item进行类型判断:
2.根据item的类型加载不同的布局:
3.根据item不同的类型添加不同的数据源:
4.在Activity中实现不同item加载不同数据源的逻辑:
5. 在GridLayout和瀑布流时让头尾宽度显示全屏,不用下面方法则显示一列的宽度
————–附效果图以及主要源码:—————–
![](http://img.blog.csdn.net/20160921032136204)
尾部
![](http://img.blog.csdn.net/20160921032213096)
注:这里每个使用的是v7包下的CardView,这不是今天的重点,所以布局文件省略。
首先我们来看一下ViewHolder类:
接下来来看看今天的猪脚:
这里的代码都经过了笔者精心注释,所以就不多解释了;
最后我们来看看Activity类:
这里就是要注意:如果头尾只一个:不添加的泛型不能填null, 可填String无不良反应,布局文件填0,数据源填null。
eg :
笔者正是考虑到数据源的潜在的复杂性,数据源或适配器的位置比较灵活,所以主观上还是觉得应该将头尾的数据源传递到适配器中,相信聪明的你一定会随机应变,实现更酷更炫的效果,笔者真心期待你的灵感的鞭策。
罪过啊,罪过……阿弥陀佛!….
废话少说,下面我们来谈正事:由于本人水平有限,希望各位大神不吝赐教,不甚感激,正所谓:“人过留名,雁过留声。”也希望各位走过路过没有错过的朋友,在评论区写下自己宝贵的想法,鞭策一下我这匹未来的千里马….谢谢;
本次封装实现了:
1. 加载各种Item布局,并做了缓存(在万能适配器的基础上封装的);
2. 选择添加头或尾(都不加,都加,二选一 均可);
3. item的点击事件;
感觉适配器的构造方法不是很理想:头尾二选一的时候必须保留适配器构造方法的三个泛型 , 并且不能设置null(不用的泛型可设置为String不影响) , 要把不选的那个资源文件设置为空,数据源设置为null,希望有识之士提出更好的写法;
让我很纠结的一点是:要不要将头尾的数据源传递到适配器中:传的话要将头尾的数据源封装成类(JavaBean)并在适配器的构造方法中作为泛型导入,比较麻烦,在通常的情况下可以直接在Activity中将数据设置到控件中,但是考虑到List等一些复杂的情况,我还是觉得该予以保留……你的看法呢?期待你的回复….
/** * 不添加头或尾的时候 * @param mContext * @param mBodyDatas * @param bodyLayoutId */ public RecyclerAdapter(Context mContext, List<T> mBodyDatas, int bodyLayoutId) { this(mContext,bodyLayoutId,mBodyDatas,0,null,0,null); } /** * 添加头或尾的时候 * @param context * @param bodyLayoutId 主体布局文件 * @param bodyDatas 主体数据源 * @param headerLayoutId 头部布局 * @param headerData 头部数据源 * @param footerLayoutId 尾部布局 * @param footerData 尾部数据源 */ public RecyclerAdapter(Context context, int bodyLayoutId, List<T> bodyDatas,int headerLayoutId,H headerData,int footerLayoutId,F footerData) { this.mContext = context; this.mBodyDatas = bodyDatas; this.mHeaderData=headerData; this.mFooterData=footerData; this.mBodyLayoutId = bodyLayoutId; this.mHeaderLayoutId = headerLayoutId; this.mFooterLayoutId = footerLayoutId; mInflater = LayoutInflater.from(mContext); //判断是否有头 if (headerLayoutId==0){ mHeaderCount=0; }else { mHeaderCount=1; } //判断是否有尾 if (footerLayoutId==0){ mBottomCount=0; }else { mBottomCount=1; } }
————-实现逻辑以及主要方法摘要:————————–
1 . 多布局,根据头尾的个数(目前只支持0或1),对每个Item进行类型判断:
//item类型分三种 public static final int ITEM_TYPE_HEADER = 0;//头 public static final int ITEM_TYPE_CONTENT = 1;//主体 public static final int ITEM_TYPE_BOTTOM = 2;//尾 private int mHeaderCount;//头部View个数 private int mBottomCount;//底部View个数 /** * 判断position对应的Item的类型 * @param position * @return */ @Override public int getItemViewType(int position) { int dataItemCount = getContentItemCount(); if (mHeaderCount != 0 && position < mHeaderCount) { //头部View return ITEM_TYPE_HEADER; } else if (mBottomCount != 0 && position >= (mHeaderCount + dataItemCount)) { //底部View return ITEM_TYPE_BOTTOM; } else { //内容View return ITEM_TYPE_CONTENT; } }
2.根据item的类型加载不同的布局:
@Override public RecycleHolder onCreateViewHolder(ViewGroup parent, int viewType) { //根据item的类型加载不同布局文件 if (viewType ==ITEM_TYPE_HEADER) { return new RecycleHolder(mInflater.inflate(mHeaderLayoutId, parent, false)); } else if (viewType == ITEM_TYPE_CONTENT) { return new RecycleHolder(mInflater.inflate(mBodyLayoutId, parent, false)); } else if (viewType == ITEM_TYPE_BOTTOM) { return new RecycleHolder(mInflater.inflate(mFooterLayoutId, parent, false)); } return null; }
3.根据item不同的类型添加不同的数据源:
@Override public void onBindViewHolder(final RecycleHolder holder, int position) { //根据item的类型实现与不同数据源进行衔接 if (isHeaderView(position)){ convertHeader(holder,mHeaderData,position); }else if (isBottomView(position)){ convertFooter(holder,mFooterData,position); }else { convertBody( holder, mBodyDatas.get(position - mHeaderCount), position); } }
4.在Activity中实现不同item加载不同数据源的逻辑:
mRecyclerView.setAdapter(adapter=new RecyclerAdapter<String,String,String>(this,stringLists,R.layout.item_recycle,R.layout.header_recycle,sHeader,R.layout.footer_recycle,sFooter){ //必须重写 @Override public void convertBody(RecycleHolder holder, String data, int position) { holder.setText(R.id.tv_item_text,data); } //选择重写 @Override public void convertHeader(RecycleHolder holder, String data, int position) { holder.setText(R.id.tv_header,sHeader); } //选择重写 @Override public void convertFooter(RecycleHolder holder, String data, int position) { holder.setText(R.id.tv_footer,sFooter); } });
5. 在GridLayout和瀑布流时让头尾宽度显示全屏,不用下面方法则显示一列的宽度
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { return (adapter.isHeaderView(position) || adapter.isBottomView(position)) ? gridLayoutManager.getSpanCount() : 1; } });
————–附效果图以及主要源码:—————–
尾部
注:这里每个使用的是v7包下的CardView,这不是今天的重点,所以布局文件省略。
首先我们来看一下ViewHolder类:
public class RecycleHolder extends RecyclerView.ViewHolder { /** 用于存储当前item当中的View */ private SparseArray<View> mViews; public RecycleHolder(View itemView) { super(itemView); mViews = new SparseArray<View>(); } public <T extends View> T findView(int ViewId) { View view = mViews.get(ViewId); //集合中没有,则从item当中获取,并存入集合当中 if (view == null) { view = itemView.findViewById(ViewId); mViews.put(ViewId, view); } return (T) view; } public RecycleHolder setText(int viewId, String text) { TextView tv = findView(viewId); tv.setText(text); return this; } public RecycleHolder setText(int viewId, int text) { TextView tv = findView(viewId); tv.setText(text); return this; } public RecycleHolder setImageResource(int viewId, int ImageId) { ImageView image = findView(viewId); image.setImageResource(ImageId); return this; } public RecycleHolder setImageBitmap(int viewId, Bitmap bitmap) { ImageView imageView= findView(viewId); imageView.setImageBitmap(bitmap); return this; } public RecycleHolder setImageNet(int viewId, String url) { final ImageView imageView= findView(viewId); //使用你所用的网络框架等,这里使用imageloader // ImageLoader.getInstance().loadImage(url, new SimpleImageLoadingListener() { // // @Override // public void onLoadingComplete(String imageUri, View view,Bitmap loadedImage) { // super.onLoadingComplete(imageUri, view, loadedImage); // imageView.setImageBitmap(loadedImage); // // } // @Override // public void onLoadingFailed(String imageUri, View view, FailReason failReason) { // super.onLoadingFailed(imageUri, view, failReason); // } // // }); return this; } }
接下来来看看今天的猪脚:
**
* Created by Jack on 2016/9/21
*/
public abstract class RecyclerAdapter<T,H,F> extends RecyclerView.Adapter<RecycleHolder> {
private Context mContext;
private List<T> mBodyDatas;
private H mHeaderData;//H 头部数据源的泛型
private F mFooterData;//T 尾部数据源的泛型
private int mBodyLayoutId,mHeaderLayoutId,mFooterLayoutId;
private LayoutInflater mInflater;
//item类型分三种
public static final int ITEM_TYPE_HEADER = 0;//头
public static final int ITEM_TYPE_CONTENT = 1;//主体
public static final int ITEM_TYPE_BOTTOM = 2;//尾
private int mHeaderCount;//头部View个数
private int mBottomCount;//底部View个数
private OnItemClickListener onItemClickListener;//item监听
/** * 不添加头或尾的时候 * @param mContext * @param mBodyDatas * @param bodyLayoutId */ public RecyclerAdapter(Context mContext, List<T> mBodyDatas, int bodyLayoutId) { this(mContext,bodyLayoutId,mBodyDatas,0,null,0,null); } /** * 添加头或尾的时候 * @param context * @param bodyLayoutId 主体布局文件 * @param bodyDatas 主体数据源 * @param headerLayoutId 头部布局 * @param headerData 头部数据源 * @param footerLayoutId 尾部布局 * @param footerData 尾部数据源 */ public RecyclerAdapter(Context context, int bodyLayoutId, List<T> bodyDatas,int headerLayoutId,H headerData,int footerLayoutId,F footerData) { this.mContext = context; this.mBodyDatas = bodyDatas; this.mHeaderData=headerData; this.mFooterData=footerData; this.mBodyLayoutId = bodyLayoutId; this.mHeaderLayoutId = headerLayoutId; this.mFooterLayoutId = footerLayoutId; mInflater = LayoutInflater.from(mContext); //判断是否有头 if (headerLayoutId==0){ mHeaderCount=0; }else { mHeaderCount=1; } //判断是否有尾 if (footerLayoutId==0){ mBottomCount=0; }else { mBottomCount=1; } }
@Override public RecycleHolder onCreateViewHolder(ViewGroup parent, int viewType) { //根据item的类型加载不同布局文件 if (viewType ==ITEM_TYPE_HEADER) { return new RecycleHolder(mInflater.inflate(mHeaderLayoutId, parent, false)); } else if (viewType == ITEM_TYPE_CONTENT) { return new RecycleHolder(mInflater.inflate(mBodyLayoutId, parent, false)); } else if (viewType == ITEM_TYPE_BOTTOM) { return new RecycleHolder(mInflater.inflate(mFooterLayoutId, parent, false)); } return null; }
@Override
public void onBindViewHolder(final RecycleHolder holder, int position) {
//根据item的类型实现与不同数据源进行衔接
if (isHeaderView(position)){
convertHeader(holder,mHeaderData,position);
}else if (isBottomView(position)){
convertFooter(holder,mFooterData,position);
}else {
convertBody( holder, mBodyDatas.get(position - mHeaderCount), position);
}
//为子项布局设置监听事件
if (onItemClickListener != null) {
//设置背景
//holder.itemView.setBackgroundResource(R.drawable.recycler_bg);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//注意,这里的position不要用上面参数中的position,会出现位置错乱\
onItemClickListener.OnItemClickListener(holder.itemView, holder.getLayoutPosition());
}
});
}
}
/**
* 将主体数据源添加到主体上-----必须要重写
* @param holder
* @param data
* @param position
*/
public abstract void convertBody(RecycleHolder holder, T data, int position);
/**
* 将头部数据源添加到头部-----recyclerView添加头部时重写
* @param holder
* @param data
* @param position
*/
public void convertHeader(RecycleHolder holder, H data, int position){
}
/**
* 将尾部数据源添加到尾部------recyclerView添加尾部时重写
* @param holder
* @param data
* @param position
*/
public void convertFooter(RecycleHolder holder, F data, int position){
}
/**
* 返回Item的个数要考虑添加头和尾的个数
* @return
*/
@Override
public int getItemCount() {
return mBodyDatas.size()+mHeaderCount+mBottomCount;
}
/**
* 返回主体item的个数
* @return
*/
public int getContentItemCount(){
return mBodyDatas.size();
}
/**
* 判断当前item是否是HeadView
* @param position
* @return
*/
public boolean isHeaderView(int position) {
return mHeaderCount != 0 && position < mHeaderCount;
}
/**
* 判断当前item是否是FootView
* @param position
* @return
*/
public boolean isBottomView(int position) {
return mBottomCount != 0 && position >= (mHeaderCount + getContentItemCount());
}
/**
* 判断position对应的Item的类型
* @param position
* @return
*/
@Override
public int getItemViewType(int position) {
int dataItemCount = getContentItemCount();
if (mHeaderCount != 0 && position < mHeaderCount) {
//头部View
return ITEM_TYPE_HEADER;
} else if (mBottomCount != 0 && position >= (mHeaderCount + dataItemCount)) {
//底部View
return ITEM_TYPE_BOTTOM;
} else {
//内容View
return ITEM_TYPE_CONTENT;
}
}
/**
* 为recycler View设置item的点击事件
* @param onItemClickListener
*/
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
//点击事件监听接口
public interface OnItemClickListener {
void OnItemClickListener(View view, int position);
}
}
这里的代码都经过了笔者精心注释,所以就不多解释了;
最后我们来看看Activity类:
public class MainActivity extends AppCompatActivity {
RecyclerView mRecyclerView;
GridLayoutManager gridLayoutManager;
RecyclerAdapter adapter;
String sHeader="我是头头,我爱你";
String sFooter="我是伟哥,我喜欢你离我远点";
List<String> stringLists;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initRecycler();
initRecyclerClick();
}
private void initRecyclerClick() {
adapter.setOnItemClickListener(new RecyclerAdapter.OnItemClickListener() {
@Override
public void OnItemClickListener(View view, int position) {
Toast.makeText(MainActivity.this, position+"我被点击了", Toast.LENGTH_SHORT).show();
}
});
}
private void initRecycler() {
mRecyclerView= (RecyclerView) findViewById(R.id.recyclerView);
stringLists=new ArrayList<>();
for (int i=0;i<30;i++) {
stringLists.add("天下第" + i);
}
//List布局
// layoutManager=new LinearLayoutManager(this);
// layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
// mRecyclerView.setLayoutManager(layoutManager);
// mRecyclerView.setAdapter(adapter=new HeaderBottomAdapter(this));
//Grid布局
gridLayoutManager=new GridLayoutManager(MainActivity.this, 2);
mRecyclerView.setLayoutManager(gridLayoutManager);//这里用线性宫格显示 类似于grid view
mRecyclerView.setAdapter(adapter=new RecyclerAdapter<String,String,String>(this,R.layout.item_recycle,stringLists,R.layout.header_recycle,sHeader,R.layout.footer_recycle,sFooter){
@Override
public void convertBody(RecycleHolder holder, String data, int position) {
holder.setText(R.id.tv_item_text,data);
}
@Override
public void convertHeader(RecycleHolder holder, String data, int position) {
holder.setText(R.id.tv_header,sHeader);
}
@Override
public void convertFooter(RecycleHolder holder, String data, int position) {
holder.setText(R.id.tv_footer,sFooter);
}
});
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { return (adapter.isHeaderView(position) || adapter.isBottomView(position)) ? gridLayoutManager.getSpanCount() : 1; } });
}
}
这里就是要注意:如果头尾只一个:不添加的泛型不能填null, 可填String无不良反应,布局文件填0,数据源填null。
eg :
adapter=new RecyclerAdapter<String,null,String>(this,R.layout.item_recycle,stringLists, 0 ,null,R.layout.footer_recycle,sFooter){ ... @Override public void convertFooter(RecycleHolder holder, String data, int position) { holder.setText(R.id.tv_footer,sFooter); } }
笔者正是考虑到数据源的潜在的复杂性,数据源或适配器的位置比较灵活,所以主观上还是觉得应该将头尾的数据源传递到适配器中,相信聪明的你一定会随机应变,实现更酷更炫的效果,笔者真心期待你的灵感的鞭策。
相关文章推荐
- RecyclerView万能适配器以及点击事件
- 为RecyclerView打造万能适配器,点击事件,5.0水波纹点击效果
- RecyclerView万能适配器,点击事件,分割线,间距等写法
- RecyclerView的使用以及下拉刷新自动加载(添加点击事件、头部)
- 为RecyclerView添加item的点击事件
- [Android | Material Design] RecyclerView Item点击事件 添加水波纹效果 两种办法
- Android 高级UI设计笔记20:RecyclerView 的详解之RecyclerView添加Item点击事件
- Android-UI布局---RecyclerView学习(一)在适配器中自定义长按和点击事件
- RecyclerView添加点击事件与效果
- 为RecyclerView添加item的点击事件
- RecyclerView加载不同item并实现其item点击事件,实现添加常用应用的功能
- 为RecyclerView添加item的点击事件
- 为RecyclerView添加item的点击事件
- RecyclerView 添加头部和尾部,并实现Item的点击事件
- RecyclerView 添加点击事件的几种方法
- Android中Recyclerview使用6----添加条目得到点击事件和长按事件(另一种写法,较简单)
- 关于recyclerview的横向布局的宽度以及item的点击事件的添加
- Android RecyclerView使用(二) -给Item添加点击事件
- RecyclerView使用(二)多种Item布局、添加点击事件
- Recyclerview添加点击事件和长按事件