Recycleview之打造通用的Adapter
Recycleview之打造通用的Adapter适配器
概述
Recycleview 是安卓当今最常用的列表,封装一个比较简便的Adapter使代码更简便更易懂,将方法封装使用只关系数据变化才是王道
废话少说直接封装BaseAdapter
1.简单BaseAdapter的实现
在继承 RecyclerView.Adapter 后 会自动生成 onCreateViewHolder、onBindViewHolder、getItemCount 方法
onCreateViewHolder 是绑定布局, onBindViewHolder绑定数据,getItemCount是返回条数
首先我们要实现adapter的初始化我们需要构造方法
/*简单初始化*/ private int mLayoutId;//布局Id private LayoutInflater mLayoutInflater;//初始化LayoutInflater 方便onCreateViewHolder初始化View public BaseAdapter(Context context, List<T> data, int layoutId) { this.mContext = context; this.mData = data; this.mLayoutId = layoutId; this.mLayoutInflater = LayoutInflater.from(context); }
接下来我们要完成onCreateViewHolder方法进行布局的绑定
这是我们需要ViewHolder来处理View的事件
@NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { if (mLayoutId == -1) throw new Resources.NotFoundException("没有找到该mLayoutId"); View view = mLayoutInflater.inflate(mLayoutId, parent, false); // 创建ViewHolder mViewHolder = new ViewHolder(view); return mViewHolder; }
所以我们在外边创建一个ViewHolder减少adapter里面的方法更简洁
public class ViewHolder extends RecyclerView.ViewHolder { // 缓存View 选择性能更好的SparseArray 代替HashMap private SparseArray <WeakReference<View>> mViews; //初始化 public ViewHolder(@NonNull View itemView) { super(itemView); mViews = new SparseArray<>(); } //获取View 如果没有就添加缓存,有的话直接取 public <T extends View> T getView(int viewId){ WeakReference<View> view = mViews.get(viewId); View view1 = null; if (view!=null){ view1 = view.get(); }else { view1 = itemView.findViewById(viewId); mViews.put(viewId,new WeakReference<View>(view1)); } return (T) view1; } // 链式调用设置文字 直接使用id来进行设置更清晰 public ViewHolder setText (int viewId ,CharSequence charSequence){ TextView view = getView(viewId); if (view==null){ throw new InflateException("设置文字时没有找到该View"); }else { view.setText(charSequence); } return this; } // 加载本地图片 public ViewHolder setImageView (int viewId ,int resId){ ImageView view = getView(viewId); if (view==null){ throw new InflateException("设置图片时没有找到该View"); }else { view.setImageResource(resId); } return this; } // 加载网络图片 ---》 创建了ImageLoader 接口 为了实现能使用各种三方库加载网络图片 public ViewHolder setImageView (int viewId ,String url,ImageLoader imageLoader){ ImageView view = getView(viewId); if (view==null){ throw new InflateException("设置图片时没有找到该View"); }else { imageLoader.loadImage(view,url); } return this; } // onBindViewHolder方法内实现点击事件 public void setOnItemClick(int viewId, final OnItemClick onItemClick){ View view = getView(viewId); view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onItemClick.click(v); } }); } // onBindViewHolder方法内实现长按点击事件 public void setOnLongItemClick(int viewId, final OnLongItemClick onLongItemClick){ View view = getView(viewId); view.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { onLongItemClick.click(v); return true; } }); } // 接口 扩展使用各种网络图片的使用 public interface ImageLoader{ void loadImage(ImageView imageView ,String url); } }
ViewHolder 实现了view的一些基础功能
回到BaseAdapter中
@Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { bindViewHolder(holder, mData.get(position), position); } /*实现bindViewHolder方法*/ protected abstract void bindViewHolder(ViewHolder holder, T t, int position);
我们将bindviewHolder方法在自己复写的Adapter中实现
同时我们将viewHolder 实体类T,以及位置信息全部传回adpter中进行界面的渲染
getItemCount肯定是返回传过来的数据
private List<T> mData; @Override public int getItemCount() { return mData.size(); }
其中为了适配在Activity中可能需要实现adpter的点击事件的问题我在BaseAdapter中添加了接口
/** * @author li * 版本:1.0 * 创建日期:2020/6/8 09 * 描述:onBindViewHolder方法内实现点击事件 */ public interface OnItemClick { void click(View view); }
/** * @author li * 版本:1.0 * 创建日期:2020/6/8 09 * 描述:onBindViewHolder方法内实现长按点击事件 */ public interface OnLongItemClick { void click(View view); }
/** * @author li * 版本:1.0 * 创建日期:2020/6/8 09 * 描述:activity内实现点击事件 */ public interface AdapterOnItemClick<T> { void click(View view,T t,int type ,int pos); }
/** * @author li * 版本:1.0 * 创建日期:2020/6/8 09 * 描述:activity内实现长按点击事件 */ public interface AdapterOnLongItemClick<T> { void click(View view, T t, int type, int pos); }
在Adpter中进行绑定接口
// 初始化Adapter的点击事件 为了避免adapter中实现activity的点击事件在activity中没有实现造成闪退问题 private AdapterOnItemClick<T> mAdapterOnItemClick = new AdapterOnItemClick<T>() { @Override public void click(View view, T t, int type, int pos) { } }; // 初始化Adapter的长按点击事件 private AdapterOnLongItemClick<T> mAdapterOnLongItemClick = new AdapterOnLongItemClick<T>() { @Override public void click(View view, T t, int type, int pos) { } }; // 设置点击事件 public void SetOnClickListener(AdapterOnItemClick<T> adapterOnItemClick) { this.mAdapterOnItemClick = adapterOnItemClick; } ; // 获取点击事件 public void setActivityItemClick(View view, T t, int type, int pos) { mAdapterOnItemClick.click(view, t, type, pos); } // 设置长点击事件 public void SetOnLongClickListener(AdapterOnLongItemClick<T> adapterOnLongItemClick) { this.mAdapterOnLongItemClick = adapterOnLongItemClick; } ; // 获取长点击事件 public void setActivityLongItemClick(View view, T t, int type, int pos) { mAdapterOnLongItemClick.click(view, t, type, pos); }
接下来为了实现数据的增删改我们添加了
// 增加 public void upData(List<T> t) { this.mData = t; notifyDataSetChanged(); } public void upData(T t) { mData.add(t); notifyDataSetChanged(); } // 修改 public void setData(int pos,T t) { mData.set(pos,t); notifyDataSetChanged(); } public void setData(T t) { int index = mData.indexOf(t); if (index>=0){ mData.set(index,t); notifyDataSetChanged(); } } // 删除 public void remove(T t) { int index = mData.indexOf(t); if (index >= 0) { mData.remove(t); notifyItemRemoved(index); } } public void remove(int index) { if (index >= 0) { mData.remove(index); notifyItemRemoved(index); } } //删除全部 public void removeAll() { this.mData.clear(); notifyDataSetChanged(); }
这样 简单的adpter就实现了
2.多布局BaseAdapter的实现
多布局就不得不说getItemViewType方法了 adpter会先调用这个方法在调用onCreateViewHolder方法
在将viewType返回过去所以我们加入了多布局的接口
/** * @author li * 版本:1.0 * 创建日期:2020/6/12 15 * 描述:实现多布局 */ public interface MixtureLayout<T> { public int getLayoutId(T data,int postion); }
/*多布局情况下初始化*/ public BaseAdapter(Context context, List<T> data, MixtureLayout<T> mixtureLayout) { this(context, data, -1); this.mMixtureLayout = mixtureLayout; }
@Override public int getItemViewType(int position) { if (mMixtureLayout != null) { // 多布局情况下返回 布局Id return mMixtureLayout.getLayoutId(mData.get(position), position); } return super.getItemViewType(position); }
在onCreateViewHolder方法中我们要新增
private MixtureLayout<T> mMixtureLayout; @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { if (mMixtureLayout != null) { mLayoutId = viewType; } if (mLayoutId == -1) throw new Resources.NotFoundException("没有找到该mLayoutId"); View view = mLayoutInflater.inflate(mLayoutId, parent, false); // 创建ViewHolder mViewHolder = new ViewHolder(view); return mViewHolder; }
到此BaseAdpter功能基本实现完成
3.BaseAdapter的使用
/** * @author li * 版本:1.0 * 创建日期:2020/6/11 20 * 描述: */ public class SimpleAdapter extends BaseAdapter<String> { private Context mContext; public SimpleAdapter(Context context, List<String> data, int layoutId) { super(context, data, layoutId); this.mContext = context; } @Override protected void bindViewHolder(ViewHolder holder, final String strings, final int position) { // 链式调用 holder.setText(R.id.tv_title1,strings) .setText(R.id.tv_title2,"哈哈哈"); // 自定义网咯图片解析方式 holder.setImageView(R.id.iv_pic, "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1591887961825&di=b46faa0b1254616dd4f1a36653f36fef&imgtype=0&src=http%3A%2F%2Fa3.att.hudong.com%2F14%2F75%2F01300000164186121366756803686.jpg" , new MyImageLoader(mContext)); // adapter 内部的点击事件 holder.setOnItemClick(R.id.iv_pic, new OnItemClick() { @Override public void click(View view) { Log.e("TAG", "click: ----------" ); // 回调到activity中 根据type判断谁点击 setActivityItemClick(view,strings,1,position); } }); holder.setOnItemClick(R.id.tv_title1, new OnItemClick() { @Override public void click(View view) { Log.e("TAG", "click: ----------" ); setActivityItemClick(view,strings,2,position); } }); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.e("TAG", "click: ----------" ); setActivityItemClick(v,strings,3,position); } }); holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { setActivityLongItemClick(v,strings,1,position); return true; } }); } }
其中MyImageLoader 我是这么实现的
/** * @author li * 版本:1.0 * 创建日期:2020/6/11 19 * 描述: */ public class MyImageLoader implements ViewHolder.ImageLoader { private Context mContext; public MyImageLoader(Context context) { this.mContext = context; } @Override public void loadImage(ImageView imageView, String url) { // 实现MyImageLoader 方法自定义网咯图片的解析方法更容易扩展 Glide.with(mContext).load(url).into(imageView); } }
简简单单几行搞定 复杂的使用
/** * @author li * 版本:1.0 * 创建日期:2020/6/11 20 * 描述: */ public class MixLayoutAdapter extends BaseAdapter<String> { private Context mContext; public MixLayoutAdapter(Context context, List<String> data) { super(context, data, new MixtureLayout<String>() { @Override public int getLayoutId(String data,int pos) { if ((pos+1)%2 ==0){ return R.layout.item_simple; }else { return R.layout.item_simple2; } } }); this.mContext = context; } @Override protected void bindViewHolder(ViewHolder holder, final String strings, final int position) { // 链式调用 //根据holder.getLayoutId()来区分不同的item 这里我使用的布局只是位置有区别 Log.e("TAG", "onCreateViewHolder: -->"+ (holder.getLayoutId()==R.layout.item_simple)+ (holder.getLayoutId()==R.layout.item_simple2)); holder.setText(R.id.tv_title1,strings) .setText(R.id.tv_title2,"哈哈哈"); // 自定义网咯图片解析方式 holder.setImageView(R.id.iv_pic, "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1591887961825&di=b46faa0b1254616dd4f1a36653f36fef&imgtype=0&src=http%3A%2F%2Fa3.att.hudong.com%2F14%2F75%2F01300000164186121366756803686.jpg" , new MyImageLoader(mContext)); // adapter 内部的点击事件 holder.setOnItemClick(R.id.iv_pic, new OnItemClick() { @Override public void click(View view) { Log.e("TAG", "click: ----------" ); // 回调到activity中 根据type判断谁点击 setActivityItemClick(view,strings,1,position); } }); holder.setOnItemClick(R.id.tv_title1, new OnItemClick() { @Override public void click(View view) { Log.e("TAG", "click: ----------" ); setActivityItemClick(view,strings,2,position); } }); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.e("TAG", "click: ----------" ); setActivityItemClick(v,strings,3,position); } }); holder.setOnLongItemClick(R.id.iv_pic, new OnLongItemClick() { @Override public void click(View view) { Log.e("TAG", "Longclick: ----------" ); setActivityLongItemClick(view,strings,1,position); } }); }
源码
- 打造通用的recycleview的adapter
- 为RecyclerView打造通用Adapter
- 打造一个通用的 RecyclerView Adapter
- 为RecyclerView打造通用Adapter 让RecyclerView更加好用
- 为RecyclerView打造通用adapter
- 打造AbsListView、RecyclerView的通用Adapter
- RecycleView在DataBinding上的通用Adapter
- 为RecyclerView打造通用Adapter 让RecyclerView更加好用
- 为RecyclerView打造通用Adapter 让RecyclerView更加好用
- HeaderRecycleAdapter--通用的带头部RecycleView.Adapter
- 为RecyclerView打造通用Adapter
- 为RecyclerView打造通用Adapter 让RecyclerView更加好用
- 为RecyclerView打造通用Adapter 让RecyclerView更加好用
- Android 通用的ViewHolder和Adapter的打造
- 打造炫酷通用的ViewPager指示器 - Adapter模式适配所有
- 打造通用的RecyclerView的Adapter
- 为RecyclerView打造通用Adapter 让RecyclerView更加好用
- 为RecyclerView打造通用Adapter
- 为RecyclerView打造通用Adapter 让RecyclerView更加好用
- RecyclerView更全解析之 - 打造通用的万能Adapter