项目实际开发过程中发现几乎每个页面都要使用到adapter,如果只是单纯的提取出一个父类BaseAdapter的话,那我们需要创建的adapter文件会很多。
所以看来是有必要封装出通用的适配器才能让自己更轻松的敲代码:
1.RecyclerView
首先看一下适用于RecyclerView的通用viewHolder:
/** * Created by Dovar on 2016/12/28 0028. * Email:[email protected] * 通用RecyclerView的ViewHolder */ public class RCommenViewHolder extends RecyclerView.ViewHolder { private SparseArray<View> mViews; private View mConvertView; public RCommenViewHolder(View itemView) { super(itemView); mConvertView = itemView; mViews = new SparseArray<View>(); } /** * 通过viewId获取控件 * * @param viewId * @return */ 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 RCommenViewHolder setText(int viewId, String text) { TextView tv = getView(viewId); tv.setText(text); return this; } public RCommenViewHolder setImageResource(int viewId, int resId) { ImageView view = getView(viewId); view.setImageResource(resId); return this; } /** * 使用glide加载网络图片 */ public void setImageUrl(Context context, int id, String url) { ImageView iv = getView(id); if (iv != null && url != null) { Glide.with(context).load(url).error(R.mipmap.ic_launcher).into(iv); } } /** * 给itemView中的子View添加点击事件 */ public RCommenViewHolder setOnClickListener(int viewId, View.OnClickListener listener) { View view = getView(viewId); view.setOnClickListener(listener); return this; } }
接下来就是我们常用到的适配器,可增加headerView、footerView
/** * Created by Dovar on 2016/12/28 0028. * Email:[email protected] * 通用RecyclerView适配器 * 本类只适用于三种布局类型:TYPE_HEADER、TYPE_FOOTER、TYPE_COMMON * 多布局类型请使用{@link MultiCommonAdapter} */ public abstract class RCommenAdapter<T> extends RecyclerView.Adapter<RCommenViewHolder> { private static final String TAG = "adapter"; protected Context mContext; private int mLayoutId; protected List<T> mDatas; private static final int TYPE_COMMON = 0; private static final int TYPE_FOOTER = 1; private static final int TYPE_HEADER = 2; private boolean hasFooter = false;//是否已添加尾布局 private boolean hasHeader = false;//是否已添加头布局 private View headerView; private View footerView; /** * 添加尾布局 * * @param footerView */ public void addFooterView(View footerView) { this.footerView = footerView; hasFooter = true; } /** * 添加头布局 * * @param headerView */ public void addHeaderView(View headerView) { this.headerView = headerView; hasHeader = true; } /** * 添加一条数据 * * @param data * @param isNotify 是否需要在添加数据后立即刷新界面 */ public void addData(T data, boolean isNotify) { if (data != null) { Log.d(TAG, "addData: " + data); mDatas.add(data); if (isNotify) { notifyDataSetChanged();//可修改为局部刷新 } } else { Log.d(TAG, "addData: data==null"); } } /** * 添加数据集合 * * @param datas 数据list * @param isClear 是否需要先清空原有数据再添加 */ public void addDatas(List<T> datas, boolean isClear) { if (isClear) { this.mDatas.clear(); } if (datas != null) { this.mDatas.addAll(datas); } else { Log.d(TAG, "addDatas: datas==null"); } notifyDataSetChanged(); } /** * 添加数据数组 * * @param datas 数据collection * @param isClear 是否需要先清空原有数据再添加 */ public void addDatas(T[] datas, boolean isClear) { if (isClear) { this.mDatas.clear(); } if (datas != null) { Collections.addAll(this.mDatas, datas); } else { Log.d(TAG, "addDatas: datas==null"); } notifyDataSetChanged(); } /** * 获取mDatas中对应下标的item * * @param pos */ public T getItem(int pos) { if (pos >= 0 && pos < mDatas.size()) { return mDatas.get(pos); } Log.d(TAG, "getItem: getItem==null"); return null; } /** * 获取mDatas * * @return */ public List<T> getDatas() { return mDatas; } /** * 返回所有item数量,包括header、footer * 此方法的返回值直接决定recyclerView的child数量 * * @return */ @Override public int getItemCount() { //header与footer都有 if (hasFooter && hasHeader) return getDataCount() + 2; //header与footer都没有 if (!hasFooter && !hasHeader) return getDataCount(); //header与footer只有一个 return getDataCount() + 1; } /** * 返回bodyItem数量 * mDatas.size() * * @return */ private int getDataCount() { if (mDatas.size() == 0) { Log.d(TAG, "getItemCount: count==0"); } return mDatas.size(); } /** * 清空数据源 */ public void clear() { if (mDatas != null) { mDatas.clear(); } else { Log.d(TAG, "clear: mDatas==null"); } } /** * 移除指定mDatas对应position上的数据 * * @param position */ public void remove(int position) { if (position >= 0 && position < mDatas.size()) { mDatas.remove(position); notifyItemRemoved(position); } } public abstract void convert(RCommenViewHolder vh, int position); public RCommenAdapter(Context context, int layoutId, List<T> datas) { mContext = context; mLayoutId = layoutId; mDatas = datas; } @Override public RCommenViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == TYPE_HEADER) { if (headerView == null) { return null; } return new RCommenViewHolder(headerView); } if (viewType == TYPE_FOOTER) { if (footerView == null) { return null; } return new RCommenViewHolder(footerView); } return new RCommenViewHolder(View.inflate(mContext, mLayoutId, null)); } @Override public void onBindViewHolder(RCommenViewHolder holder, int position) { if (getItemViewType(position) == TYPE_HEADER) return; if (getItemViewType(position) == TYPE_FOOTER) return; if (hasHeader) {//有header时,header对应的position为0,其他从1开始 convert(holder, position - 1); } else { convert(holder, position); } } @Override public int getItemViewType(int position) { if (headerView != null && hasHeader && position == 0) return TYPE_HEADER; if (footerView != null && hasFooter && position == getItemCount() - 1) return TYPE_FOOTER; return TYPE_COMMON; } /** * GridManager时设置header、footer跨列 * * @param recyclerView */ @Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); RecyclerView.LayoutManager manager = recyclerView.getLayoutManager(); if (manager instanceof GridLayoutManager) { final GridLayoutManager gridManager = ((GridLayoutManager) manager); gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { switch (getItemViewType(position)) { case TYPE_FOOTER: case TYPE_HEADER: //设置跨列 return gridManager.getSpanCount(); default: return 1; } } }); } } }
上面这是适用于RecyclerView中只有一种布局的情况,接下来就是适用于多种布局的通用适配器了:
/** * Created by Dovar on 2016/12/28 0028. * Email:[email protected] * 通用RecyclerView多布局适配器 * */ public abstract class MultiCommonAdapter<T> extends RCommenAdapter<T> { public MultiCommonAdapter(Context context, List<T> datas) { super(context, -1, datas); } @Override public int getItemViewType(int position) { return getItemType(position); } public abstract int getItemType(int position); //利用viewType返回layoutId,直接生成viewHolder @Override public RCommenViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new RCommenViewHolder(View.inflate(mContext, viewType, null)); } @Override public void onBindViewHolder(RCommenViewHolder holder, int position) { convert(holder, position); } }
2.ListView
首先是ListView的通用viewHolder:
/** * listView 通用ViewHolder * 与CommenAdapter结合使用 * Created by Dovar on 2016/11/22 0022. */ public class CommenViewHolder { private View convertView; private SparseArray<View> views;//储存子view,复用,省去重复的findViewById public CommenViewHolder(View convertView) { this.convertView = convertView; this.views = new SparseArray<>(); } public static CommenViewHolder get(View convertView, Context mContext, int resouce) { CommenViewHolder vh; if (convertView == null) { convertView = View.inflate(mContext, resouce, null); vh = new CommenViewHolder(convertView); convertView.setTag(vh); } else { vh = (CommenViewHolder) convertView.getTag(); } return vh; } public View getConvertView() { return convertView; } public <T extends View> T getView(int id) { View view = views.get(id); if (view == null) { view = convertView.findViewById(id); views.put(id, view); } return (T) view; } public void setText(int id, String text) { TextView tv = getView(id); if (tv != null) tv.setText(text); } public void setImageResource(int id, int drawableId) { ImageView iv = getView(id); if (iv != null) iv.setImageResource(drawableId); } /** * 使用glide加载网络图片 */ public void setImageUrl(Context context, int id, String url) { ImageView iv = getView(id); if (iv != null && url != null) { Glide.with(context).load(url).error(com.youmasc.youma.R.mipmap.ic_launcher).into(iv); } } public void setOnClickListener(int viewId, View.OnClickListener listener) { View view = getView(viewId); view.setOnClickListener(listener); } }
然后,就是ListView的万能适配器了:
/** * ListView通用adapter * 与CommenViewHolder结合使用 * Created by Dovar on 2016/11/22 0022. */ public abstract class CommenAdapter<T> extends BaseAdapter { protected List<T> mList; protected Context mContext; protected int mResouce; protected final String TAG = "tag"; public CommenAdapter(List<T> mList, Context mContext, int mResouce) { this.mList = mList; this.mContext = mContext; this.mResouce = mResouce; } public void addDatas(List<T> list, boolean isClear) { if (isClear) { mList.clear(); } if (list != null) { mList.addAll(list); } notifyDataSetChanged(); } public void addDatas(T[] array) { mList.clear(); if (array != null) { Collections.addAll(mList, array); notifyDataSetChanged(); } } public void addData(T clazz) { if (clazz != null) { mList.add(clazz); } } public void remove(T clazz){ mList.remove(clazz); } public void replace(int position, T data) { if (position < getCount()) { mList.remove(position); mList.add(position, data); notifyDataSetChanged(); } } public List<T> getList() { return mList; } public void clear() { mList.clear(); } @Override public int getCount() { return mList.size(); } @Override public T getItem(int position) { return mList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (getCount() == 0) return null; CommenViewHolder viewHolder = CommenViewHolder.get(convertView, mContext, mResouce); convert(viewHolder, position); return viewHolder.getConvertView(); } public abstract void convert(CommenViewHolder vh, int position); }
好了,比较直接,基本都是直接贴代码,关键方法都注释上了,相信基本也都能看明白
如果还有哪里有疑问的或者发现代码有问题的,欢迎评论里讨论