一步步打造Android RecyclerView万能适配器
2016-12-21 15:10
597 查看
转载请注明出处:
http://blog.csdn.net/u014702332/article/details/53785136
本文出版[扫地僧的博客]
首先查看效果图,依次是线性线性垂直布局,水平布局,网络布局,瀑布流布局
在布局文件中添加这个文件
然后把这个控件添加到布局文件中
上面这些代码其实很好理解,创建一个FruitAdapter 继承自RecyclerView.Adapter,这个时候会提示我们需要实现三个方法:
onCreateViewVolder看名字知道要创建一个ViewHolder,然后并返回
onBindViewHolder 给ViewHolder 绑定数据
getItemCount不需要做任何解释。
接下来我们需要显示ViewHolder中的控件
创建了一个FruitViewHolder 继承自 RecyclerView.ViewHolder 然后这里会提示需要实现一个构造方法。而构造方法中的View 正是我们要显示控件的根布局,
这里我们就可以通过根布局来创建相应的控件。
我们之前创建的FruiAdapter 中都是用RecyclerView.ViewHolder 这是所有ViewHolder的基类,如果我们需要绑定FruiBean的数据类型,就必须给FruitAdapter中ViewHolder 指定ViewHolder类型,所以我们的代码更改后变成如下:
我们发现这个Adapter里面其实有很多冗余代码,如果我以后还要写其它的adapter就会产生一堆冗余代码,因此我们需要封装一层,以前我自己封装过ListView中的Adapter,RecyclerView.Adapter也差不多,
上面代码说明:
创建RecyclerView.ViewHolder子类
获取每一个子类控件
常用的文字与图片设置方法
子控件的点击事件,我这里传了3个参数,控件的Id,对象,监听,一般用到点击事件的时候,都需要绑定数据
代码解读:
1. 首先创建RecyclerAdapter的子类的抽象类,把一些需要实现的方法给子类去实现
2. 添加构造方法,其中构造函数包括Context,list,布局资源 这样我们可以根据自己的需要传入相应数据类型,以及布局
3. 在onBindViewHolder中添加数据以及绑定事件,另外事件的回调函数一定要判断不为空才可以调用回调方法
给Item 添加回调,使item可以点击以及长按
另外也不要在onCreateView中获取某个实体类,很容易造成数组越界,因为这个时候getAdapterPosition为-1 在创建之前布局还没有完成,
代码解读:
首先基础FruitAdapter继承于BaseRVAdapter,并实现其2个抽象方法
在bindAction方法中绑定子控件的点击事件,在bindData方法中绑定数据
另外实现OnClick接口,来完成点击事件
最后使用我们创建的万能基类实际调用:
可以看到适配器用的是FruitAdapter2
给Adaapter绑定回调方法,使item可以点击
最后再看看效果
代码地址
http://blog.csdn.net/u014702332/article/details/53785136
本文出版[扫地僧的博客]
基础部分
引入support包
要使用RecyclerView首先需要引入support包:compile ‘com.android.support:recyclerview-v7:25.1.0’首先查看效果图,依次是线性线性垂直布局,水平布局,网络布局,瀑布流布局
在布局文件中添加这个文件
然后把这个控件添加到布局文件中
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_my_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="king.com.recyler.RecyclerhorizontalActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="wrap_content"/> </RelativeLayout>
代码中使用并声明这个控件
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view); 线性布局 LinearLayoutManager layoutManager = new LinearLayoutManager(this); layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); //水平布局,可以左右滑动 layoutManager.setOrientation(LinearLayoutManager.VERTICAL); //垂直布局,可以上下滑动,类似于ListView //网络布局, GridLayoutManager layoutManager = new GridLayoutManager(this,5); //后面这个参数,表示网格有多少列,当前表示5列 //瀑布流布局 StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL); // 前一个参数表示有多少列,后一个参数表示瀑布流的方式;我这里表示有3列,瀑布流方向是垂直方向 recyclerView.setLayoutManager(layoutManager); FruitAdapter adapter = new FruitAdapter(FruitBean.getFruitBeans(),R.layout.fruit_item_horizontal); recyclerView.setAdapter(adapter);
构造数据
public class FruitBean{ public int id; public String name; public int resourceId; /** * 创建数据 * @return */ public static List<FruitBean> getFruitBeans(){ List<FruitBean> fruitBeanList = new ArrayList<>(); for(int i = 0;i<100;i++){ FruitBean bean = new FruitBean(); bean.id = i; switch (i%10){ case 0: bean.name = "apple"; bean.resourceId = R.drawable.apple_pic; break; case 1: bean.name = "banana"; bean.resourceId = R.drawable.banana_pic; break; case 2: bean.name = "cherry"; bean.resourceId = R.drawable.cherry_pic; break; case 3: bean.name = "grape"; bean.resourceId = R.drawable.grape_pic; break; case 4: bean.name = "mango"; bean.resourceId = R.drawable.mango_pic; break; case 5: bean.name = "orange"; bean.resourceId = R.drawable.orange_pic; break; case 6: bean.name = "pear"; bean.resourceId = R.drawable.pear_pic; break; case 7: bean.name = "pineapple"; bean.resourceId = R.drawable.pineapple_pic; break; case 8: bean.name = "strawberry"; bean.resourceId = R.drawable.strawberry_pic; break; case 9: bean.name = "watermelon"; bean.resourceId = R.drawable.watermelon_pic; break; default: bean.name = "apple"; bean.resourceId = R.drawable.apple_pic; break; } fruitBeanList.add(bean); } return fruitBeanList; } /** * 创建数据,流式布局需要使用的。 * @return */ public static List<FruitBean> getStaggeredFruitBeans(){ List<FruitBean> fruitBeanList = new ArrayList<>(); for(int i = 0;i<100;i++){ FruitBean bean = new FruitBean(); bean.id = i; switch (i%10){ case 0: bean.name = "apple"; bean.resourceId = R.drawable.apple_pic; break; case 1: bean.name = "banana"; bean.resourceId = R.drawable.banana_pic; break; case 2: bean.name = "cherry"; bean.resourceId = R.drawable.cherry_pic; break; case 3: bean.name = "grape"; bean.resourceId = R.drawable.grape_pic; break; case 4: bean.name = "mango"; bean.resourceId = R.drawable.mango_pic; break; case 5: bean.name = "orange"; bean.resourceId = R.drawable.orange_pic; break; case 6: bean.name = "pear"; bean.resourceId = R.drawable.pear_pic; break; case 7: bean.name = "pineapple"; bean.resourceId = R.drawable.pineapple_pic; break; case 8: bean.name = "strawberry"; bean.resourceId = R.drawable.strawberry_pic; break; case 9: bean.name = "watermelon"; bean.resourceId = R.drawable.watermelon_pic; break; default: bean.name = "apple"; bean.resourceId = R.drawable.apple_pic; break; } //这里处理了一下名字长度,区别流式布局 Random random = new Random(); int length = random.nextInt(10)+1; StringBuilder sb = new StringBuilder(); for (int j=0;j<length;j++){ sb.append(bean.name); } bean.name = sb.toString(); fruitBeanList.add(bean); } return fruitBeanList; }
创建数据适配器
public class FruitAdapter extends RecyclerView.Adapter { @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return null; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } @Override public int getItemCount() { return 0; } }
上面这些代码其实很好理解,创建一个FruitAdapter 继承自RecyclerView.Adapter,这个时候会提示我们需要实现三个方法:
onCreateViewVolder看名字知道要创建一个ViewHolder,然后并返回
onBindViewHolder 给ViewHolder 绑定数据
getItemCount不需要做任何解释。
接下来我们需要显示ViewHolder中的控件
/** * ViewHolder用于存储列表项中显示的控件, */ static class FruitViewHolder extends RecyclerView.ViewHolder{ ImageView ivImg; TextView tvNo,tvName; /** * itemView 就是控件外层布局, * @param itemView */ public FruitViewHolder(View itemView) { super(itemView); ivImg = (ImageView) itemView.findViewById(R.id.iv_img); tvNo = (TextView) itemView.findViewById(R.id.tv_no); tvName = (TextView) itemView.findViewById(R.id.tv_name); } }
创建了一个FruitViewHolder 继承自 RecyclerView.ViewHolder 然后这里会提示需要实现一个构造方法。而构造方法中的View 正是我们要显示控件的根布局,
这里我们就可以通过根布局来创建相应的控件。
我们之前创建的FruiAdapter 中都是用RecyclerView.ViewHolder 这是所有ViewHolder的基类,如果我们需要绑定FruiBean的数据类型,就必须给FruitAdapter中ViewHolder 指定ViewHolder类型,所以我们的代码更改后变成如下:
package king.com.recyler; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import java.util.List; /** * Created by liuking on 16/12/20. * 创建Adapter */ public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.FruitViewHolder> { private List<FruitBean> fruitBeanList; private int mLayoutId; public FruitAdapter(List<FruitBean> list,int layoutId){ this.fruitBeanList = list; this.mLayoutId = layoutId; } /** * 构建一个ViewHolder布局 * @param parent * @param viewType * @return */ @Override public FruitAdapter.FruitViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(mLayoutId,parent,false); FruitViewHolder holder = new FruitViewHolder(view); //创建一个View对象 return holder; } /** * 绑定数据 * @param holder * @param position */ @Override public void onBindViewHolder(FruitViewHolder holder, int position) { final FruitBean bean = fruitBeanList.get(position); holder.ivImg.setImageResource(bean.resourceId); holder.tvName.setText(bean.name); holder.tvNo.setText("编号:"+bean.id); } @Override public int getItemCount() { return fruitBeanList.size(); } /** * ViewHolder用于存储列表项中显示的控件, */ static class FruitViewHolder extends RecyclerView.ViewHolder{ ImageView ivImg; TextView tvNo,tvName; /** * itemView 就是控件外层布局, * @param itemView */ public FruitViewHolder(View itemView) { super(itemView); ivImg = (ImageView) itemView.findViewById(R.id.iv_img); tvNo = (TextView) itemView.findViewById(R.id.tv_no); tvName = (TextView) itemView.findViewById(R.id.tv_name); } } }
设置点击事件
至此基础工作已经全部完成了,但我们会发现有些问题。所有的item 都不能点击,这是为什么呢,像ListView每个item都可以点击,为什么RecyclerView就不能点击? 其实Listview 的设计并不是很完美,如果我想点击ListView中的某个item中的button呢,虽然可以 实现,但相对来说是比较麻烦的,而RecyclerView就直接放弃了这样做法,让开发者自己去设定控件每一个点击事件,轻松实现点击:/** * 构建一个ViewHolder布局 * @param parent * @param viewType * @return */ @Override public FruitAdapter.FruitViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(mLayoutId,parent,false); final FruitViewHolder holder = new FruitViewHolder(view); //创建一个View对象 holder.mItemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int position = holder.getAdapterPosition(); FruitBean fb = fruitBeanList.get(position); Toast.makeText(parent.getContext(), "click item"+position, Toast.LENGTH_SHORT).show(); } }); holder.ivImg.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int position = holder.getAdapterPosition(); FruitBean fb = fruitBeanList.get(position); Toast.makeText(parent.getContext(), "click item"+fb.name, Toast.LENGTH_SHORT).show(); } }); return holder; } /** * ViewHolder用于存储列表项中显示的控件, */ static class FruitViewHolder extends RecyclerView.ViewHolder{ View mItemView; ImageView ivImg; TextView tvNo,tvName; /** * itemView 就是控件外层布局, * @param itemView */ public FruitViewHolder(View itemView) { super(itemView); mItemView = itemView; ivImg = (ImageView) itemView.findViewById(R.id.iv_img); tvNo = (TextView) itemView.findViewById(R.id.tv_no); tvName = (TextView) itemView.findViewById(R.id.tv_name); } }
我们发现这个Adapter里面其实有很多冗余代码,如果我以后还要写其它的adapter就会产生一堆冗余代码,因此我们需要封装一层,以前我自己封装过ListView中的Adapter,RecyclerView.Adapter也差不多,
打造常用RecyclerAdapter与RecyclerView.ViewHoder基类
1. recyclerView.ViewHoder类构建
首先我们需要创建一个继承于RecyclerView.ViewHolder的类public class RVViewHolder extends RecyclerView.ViewHolder { private Context mContext; public RVViewHolder(Context context, View itemView) { super(itemView); this.mContext = context; } /** * 通过viewId获取控件 * * @param viewId * @return */ public <T extends View> T getView(int viewId) { View view = itemView.findViewById(viewId); return (T) view; } public RVViewHolder setImageRes(int viewId, int imgRes) { ImageView view = getView(viewId); view.setImageResource(imgRes); return this; } public RVViewHolder setImageUrl(int viewId, String imageUrl) { ImageView view = getView(viewId); return this; } public RVViewHolder setText(int viewId, String text) { TextView tv = getView(viewId); tv.setText(text); return this; } public RVViewHolder setTextColor(int viewId, int textColor) { TextView view = getView(viewId); view.setTextColor(textColor); return this; } public RVViewHolder setTextSize(int viewId, float size) { TextView view = getView(viewId); view.setTextSize(size); return this; } public RVViewHolder setTextColorRes(int viewId, int textColorRes) { TextView view = getView(viewId); view.setTextColor(mContext.getResources().getColor(textColorRes)); return this; } public RVViewHolder setVisible(int viewId, boolean visible) { View view = getView(viewId); view.setVisibility(visible ? View.VISIBLE : View.GONE); return this; } public RVViewHolder setVisiblePlaceHolder(int viewId, boolean visible) { View view = getView(viewId); view.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); return this; } /** * 功能描述:设置各个子控件的点击事件 * 作者: hg_liuzl@qq.com * 时间:2016/12/21 13:28 * 参数: */ public RVViewHolder setOnClickListener(int viewId, Object o, View.OnClickListener listener) { View view = getView(viewId); view.setTag(o); view.setOnClickListener(listener); return this; } }
上面代码说明:
创建RecyclerView.ViewHolder子类
获取每一个子类控件
常用的文字与图片设置方法
子控件的点击事件,我这里传了3个参数,控件的Id,对象,监听,一般用到点击事件的时候,都需要绑定数据
2. recyclerViewAdapter 基类构造
先上代码package king.com.recyler; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import java.util.List; /** * @work: 构建RecyclerView.Adapter基类 * @author: hg_liuzl(hg_liuzl@qq.com) * @date: created at 2016/12/21 11:14 */ public abstract class BaseRVAdapter<T> extends RecyclerView.Adapter<RVViewHolder> { protected List<T> mList; //数据集合 protected int resLayout; //布局资源 protected Context mContext; protected IRecyclerViewListener recyclerViewListener; public BaseRVAdapter(Context context, List<T> mList, int resLayout) { this.mContext = context; this.mList = mList; this.resLayout = resLayout; } @Override public RVViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(mContext).inflate(resLayout, parent, false); final RVViewHolder viewHolder = new RVViewHolder(mContext, view); return viewHolder; } @Override public void onBindViewHolder(final RVViewHolder viewHolder, int position) { bindAction(viewHolder, mList.get(viewHolder.getLayoutPosition())); /**绑定事件 一定要在这里绑定**/ if (null != recyclerViewListener) { viewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { recyclerViewListener.onItemClick(viewHolder.itemView, viewHolder.getLayoutPosition()); } }); viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View view) { recyclerViewListener.onItemLongClick(viewHolder.itemView, viewHolder.getLayoutPosition()); return false; } }); } bindData(viewHolder, mList.get(position)); } /** * 绑定数据 **/ public abstract void bindData(RVViewHolder viewHolder, T bean); /** * 绑定动作 **/ public abstract void bindAction(RVViewHolder viewHolder, T bean); @Override public int getItemCount() { if (null != mList) { return mList.size(); } return 0; } /** * 设置点击事件 * * @param mIRecyclerViewListener */ public void setIRecyclerViewListener(IRecyclerViewListener mIRecyclerViewListener) { this.recyclerViewListener = mIRecyclerViewListener; } public interface IRecyclerViewListener { void onItemClick(View view, int position); void onItemLongClick(View view, int position); } }
代码解读:
1. 首先创建RecyclerAdapter的子类的抽象类,把一些需要实现的方法给子类去实现
2. 添加构造方法,其中构造函数包括Context,list,布局资源 这样我们可以根据自己的需要传入相应数据类型,以及布局
3. 在onBindViewHolder中添加数据以及绑定事件,另外事件的回调函数一定要判断不为空才可以调用回调方法
给Item 添加回调,使item可以点击以及长按
3. recycler的getAdapterPostion与getLayoutPosition()坑点说明
根据官方说明getAdapterPosition是在变化的时候就能获取到position,而getLayoutPosition是在布局完成后可以获取到position,一般我们需要获取某个position的时候,肯定是在布局完成后获取,所以建议使用getLayoutPosition来获取position另外也不要在onCreateView中获取某个实体类,很容易造成数组越界,因为这个时候getAdapterPosition为-1 在创建之前布局还没有完成,
在子类中继承万能RecyclerViewAdapter
package king.com.recyler; import android.content.Context; import android.view.View; import android.widget.Toast; import java.util.List; /** * * @work: ${功能介绍} * @author: hg_liuzl(hg_liuzl@qq.com) * @date: created at 2016/12/21 12:48 */ public class FruitAdapter2 extends BaseRVAdapter implements View.OnClickListener { public FruitAdapter2(Context context, List mList, int resLayout) { super(context, mList, resLayout); } @Override public void bindAction(RVViewHolder viewHolder, Object o) { viewHolder.setOnClickListener(R.id.tv_name, o, this); } @Override public void bindData(RVViewHolder viewHolder, Object o) { final FruitBean bean = (FruitBean) o; viewHolder.setText(R.id.tv_name, bean.name); viewHolder.setText(R.id.tv_no, "编号:" + bean.id); viewHolder.setImageRes(R.id.iv_img, bean.resourceId); } @Override public void onClick(View view) { final FruitBean bean = (FruitBean) view.getTag(); switch (view.getId()) { case R.id.tv_name: Toast.makeText(mContext, "你点击了Item中的" + bean.id + "----name是" + bean.name, Toast.LENGTH_SHORT).show(); break; } } }
代码解读:
首先基础FruitAdapter继承于BaseRVAdapter,并实现其2个抽象方法
在bindAction方法中绑定子控件的点击事件,在bindData方法中绑定数据
另外实现OnClick接口,来完成点击事件
最后使用我们创建的万能基类实际调用:
/** * 垂直布局 */ public class RecyclerVerticalActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my_recycler_view); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view); LinearLayoutManager layoutManager = new LinearLayoutManager(this); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(layoutManager); FruitAdapter2 adapter = new FruitAdapter2(this, FruitBean.getFruitBeans(), R.layout.fruit_item_vertical); recyclerView.setAdapter(adapter); adapter.setIRecyclerViewListener(recyclerViewListener); } private BaseRVAdapter.IRecyclerViewListener recyclerViewListener = new BaseRVAdapter.IRecyclerViewListener() { @Override public void onItemClick(View view, int position) { Toast.makeText(RecyclerVerticalActivity.this, position + " click", Toast.LENGTH_SHORT).show(); } @Override public void onItemLongClick(View view, int position) { Toast.makeText(RecyclerVerticalActivity.this, position + " longClick", Toast.LENGTH_SHORT).show(); } }; }
可以看到适配器用的是FruitAdapter2
给Adaapter绑定回调方法,使item可以点击
最后再看看效果
代码地址
相关文章推荐
- Android打造万能适配器--RecyclerView
- android 用mvp模式来架构自己的app+打造Recyclerview万能适配器
- 打造android偷懒神器———RecyclerView的万能适配器
- Android 打造自己的解耦JavaBean的RecyclerView万能适配器(三)
- Android 推荐几款好用的开源作品(二)之万能RecyclerView适配器
- [置顶] ScratchView:一步步打造万能的 Android 刮奖效果控件
- Android 轻量级万能适配器,通吃所有的 AbsListView、RecyclerView
- Android(无需javabean,支持多item type)的RecyclerView万能适配器,解耦从这里做起
- 为RecyclerView打造简单易用扩展性高的万能适配器
- Android——RecyclerView——Adapter完全图解,带你彻底掌握万能的适配器
- android RecyclerView一步步打造分组效果、类似QQ分组、折叠菜单、分组效果(一)
- ScratchView:一步步打造万能的 Android 刮奖效果控件
- 偷懒新姿势,打造属于RecyclerView的万能适配器Adapter和ViewHolder
- 偷懒新姿势,打造属于RecyclerView的万能适配器Adapter和ViewHolder
- Databinding打造RecyclerView万能适配器
- 为RecyclerView打造万能适配器
- ScratchView:一步步打造万能的 Android 刮奖效果控件
- 自定义控件 ScratchView:一步步打造万能的 Android 刮奖效果控件
- Android开发之实现RecyclerView万能适配器
- (再次更新)打造RecyclerView万能适配器,上拉刷新,下拉加载