商城项目实战 | 9.1 Adapter 封装的全面解析
来源:互联网 发布:数据机房除尘 编辑:程序博客网 时间:2024/05/20 03:43
本文为菜鸟窝作者刘婷的连载。”商城项目实战”系列来聊聊仿”京东淘宝的购物商城”如何实现。
在之前的文章《商城项目实战 | 6.2 OkHttp 轻松封装 更加灵活的调用》中已经介绍了封装的好处和意义,同时也讲解了网络请求框架 OkHttp 的封装过程,而在这篇文章中则是要进一步讲解如何封装 Adapter,主要针对于 RecyclerView.Adapter 的封装。
封装 Adapter
之前的文章《商城项目实战 | 6.2 OkHttp 轻松封装 更加灵活的调用》已经详细介绍了封装的好处和意义,为了让代码更为的简洁化和易维护性,我们对 OkHttp 进行了封装,同样的目的,我们需要对 Adapter 进行封装。
1. 定义 BaseViewHolder
RecyclerView 的 Adapter 创建时都需要写一个单独的 RecyclerView.ViewHolder,封装 Adapter 的话,自然要先封装好相应的 ViewHolder,以便后面可以重用。创建共同的 ViewHolder,命名为 BaseViewHolder,同时继承于 RecyclerView.ViewHolder,代码如下。
public class BaseViewHolder extends RecyclerView.ViewHolder { private SparseArray<View> views; public BaseViewHolder(View itemView){ super(itemView); this.views = new SparseArray<View>(); } public TextView getTextView(int viewId) { return retrieveView(viewId); } public Button getButton(int viewId) { return retrieveView(viewId); } public ImageView getImageView(int viewId) { return retrieveView(viewId); } public View getView(int viewId) { return retrieveView(viewId); } protected <T extends View> T retrieveView(int viewId) { View view = views.get(viewId); if (view == null) { view = itemView.findViewById(viewId); views.put(viewId, view); } return (T) view; }}
因为在 item 的布局文件中不知道具体会有多少相应的 View,所以在 ViewHolder 中使用了 SparseArray 来装载相应的 View 集合。另外,对于控件的调用如果每次都写一次也太过于繁琐了,所以这里也直接使用 retrieveView(int viewId) 方法传入 View 的 id 值,并且也可以根据所要返回的不同控件来写相应的方法,例如 getImageView() 直接返回 ImageView,而 getTextView() 直接返回 TextView。
TextView textView = viewHolder.getTextView(R.id.text);
上面是 getTextView() 方法的调用,返回 TextView, 后期在 Adapter 中对于控件的处理都可以这样调用,非常简单,也不用多写重复的代码。
2. 定义 BaseAdapter
已经基本定义好了 BaseViewHolder 了,下面就在新创建的 BaseAdapter 中使用它。新建 BaseAdapter,然后继承于 RecyclerView.Adapter ,同时数据类型传入泛型,这样不同数据类型的 List 也可以直接传入到 BaseAdapter 中了。
public abstract class BaseAdapter<T, H extends BaseViewHolder> extends RecyclerView.Adapter<BaseViewHolder> { protected static final String TAG = BaseAdapter.class.getSimpleName(); protected final Context context; protected final int layoutResId; protected List<T> datas; public BaseAdapter(Context context, int layoutResId) { this(context, layoutResId, null); } public BaseAdapter(Context context, int layoutResId, List<T> datas) { this.datas = datas == null ? new ArrayList<T>() : datas; this.context = context; this.layoutResId = layoutResId; } @Override public BaseViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View view = LayoutInflater.from(viewGroup.getContext()).inflate(layoutResId, viewGroup, false); BaseViewHolder vh = new BaseViewHolder(view, mOnItemClickListener); return vh; } @Override public void onBindViewHolder(BaseViewHolder viewHoder, int position) { T item = getItem(position); convert((H) viewHoder, item); } @Override public int getItemCount() { if (datas == null || datas.size() <= 0) return 0; return datas.size(); } public T getItem(int position) { if (position >= datas.size()) return null; return datas.get(position); } /** * Implement this method and use the helper to adapt the view to the given item. * * @param viewHoder A fully initialized helper. * @param item The item that needs to be displayed. */ protected abstract void convert(H viewHoder, T item);}
在 BaseAdapter 中写好 Adapter 的一些基本方法,同时这里的抽象方法 convert (H viewHoder, T item) 主要是用于继承于 BaseAdapter 的子类可以扩展写入布局中控件的处理以及数据列表的装载。
3. 加入点击事件
在 RecyclerView 的 Adapter 中和 ListView 的 Adapter 有个很大的不同之处就是 ListView 本身提供了一个 setOnItemClickListener(AdapterView.OnItemClickListener listener) 方法用于对选项的点击事件的监听,但是 RecyclerView 的 item 项的点击事件需要自己定义,所以在封装的 BaseAdapter 中肯定要加入了。
private OnItemClickListener mOnItemClickListener = null; public interface OnItemClickListener { void onItemClick(View view, int position); } public void setOnItemClickListener(OnItemClickListener listener) { this.mOnItemClickListener = listener; }
而这个事件监听最终是要作用于选项的点击,所以在 BaseViewHolder 也需要添加点击事件的监听,首先声明监听事件。
private BaseAdapter.OnItemClickListener mOnItemClickListener;
BaseViewHolder 中 implements View.OnClickListener ,同时之前 BaseViewHolder 的构造方法修改如下。
public BaseViewHolder(View itemView, BaseAdapter.OnItemClickListener onItemClickListener) { super(itemView); itemView.setOnClickListener(this); this.mOnItemClickListener = onItemClickListener; this.views = new SparseArray<View>(); } @Override public void onClick(View v) { if (mOnItemClickListener != null) { mOnItemClickListener.onItemClick(v, getLayoutPosition()); } }
因为BaseViewHolder 实现了 View.OnClickListener,所以这里自然要复写 onClick 方法,在这里方法中实现 item 的点击事件。
4. 添加基本的数据操作方法
数据操作主要是增删改查,我们的商城项目中主要是列表的下拉刷新以及加载更多的处理,这里就在 BaseAdapter 中写入数据的增删操作方法。
public void clearData() { int itemCount = datas.size(); datas.clear(); this.notifyItemRangeRemoved(0, itemCount); } public List<T> getDatas() { return datas; } public void addData(List<T> datas) { addData(0, datas); } public void addData(int position, List<T> datas) { if (datas != null && datas.size() > 0) { this.datas.addAll(datas); this.notifyItemRangeChanged(position, datas.size()); } }
clearData() 方法是用于清除数据的,addData(List datas) 方法则是添加数据,另外 getDatas() 方法可以直接获取对应 Adapter 中的数据列表,以便后面对于数据的获取。
5. 添加 SimpleAdapter
写好了 BaseAdapter 还是不够完善,还希望 Adapter 的实现和调用更加简单些,就要在添加一个 SimpleAdapter,用于继承 BaseAdapter。
public abstract class SimpleAdapter<T> extends BaseAdapter<T, BaseViewHolder> { public SimpleAdapter(Context context, int layoutResId) { super(context, layoutResId); } public SimpleAdapter(Context context, int layoutResId, List<T> datas) { super(context, layoutResId, datas); }}
之后新增的 Adapter 都继承于 SimpleAdapter 就可以了,更为的方便了。
使用封装好的 Adapter
在文章《商城项目实战 | 8.2 SwipeRefreshLayout 实现可以下拉刷新和加载更多的热门商品列表》中实现了可以下拉刷新和加载更多的热门商品列表,相应列表的 Adapter 的实现写得比较繁琐,现在就通过使用封装好的 Adapter 来使代码简单起来。
1. 实现热门商品列表的 Adapter
之前在里面定义的列表 Adapter 的代码如下。
public class HotWaresAdapter extends RecyclerView.Adapter<HotWaresAdapter.ViewHolder> { private List<WaresInfo> mDatas; private LayoutInflater mInflater; public HotWaresAdapter(List<WaresInfo> wares){ mDatas = wares; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { mInflater = LayoutInflater.from(parent.getContext()); View view = mInflater.inflate(R.layout.recycler_item_wares_layout,null); return new ViewHolder(view); } @Override public void onBindViewHolder(ViewHolder holder, int position) { WaresInfo wares = getData(position); holder.draweeView.setImageURI(Uri.parse(wares.getImgUrl())); holder.textTitle.setText(wares.getName()); holder.textPrice.setText("¥"+wares.getPrice()); } public WaresInfo getData(int position){ return mDatas.get(position); } public List<WaresInfo> getDatas(){ return mDatas; } public void clearData(){ mDatas.clear(); notifyItemRangeRemoved(0,mDatas.size()); } public void addData(List<WaresInfo> datas){ addData(0,datas); } public void addData(int position,List<WaresInfo> datas){ if(datas !=null && datas.size()>0) { mDatas.addAll(datas); notifyItemRangeChanged(position, mDatas.size()); } } @Override public int getItemCount() { if(mDatas!=null && mDatas.size()>0) return mDatas.size(); return 0; } class ViewHolder extends RecyclerView.ViewHolder{ SimpleDraweeView draweeView; TextView textTitle; TextView textPrice; public ViewHolder(View itemView) { super(itemView); draweeView = (SimpleDraweeView) itemView.findViewById(R.id.drawee_view); textTitle= (TextView) itemView.findViewById(R.id.text_title); textPrice= (TextView) itemView.findViewById(R.id.text_price); } }}
这也是我们一般定义列表 Adapter 所要写的。现在我们封装了 Adapter 就直接使用起来看下,同样也是现实可以下拉刷新和加载更多的热门商品列表 Adapter,但是我们所要实现的代码却大大的简洁了,新建 HWAdapter,具体实现如下。
public class HWAdapter extends SimpleAdapter<WaresInfo> { public HWAdapter(Context context, List<WaresInfo> datas) { super(context, R.layout.recycler_item_wares_layout, datas); } @Override protected void convert(BaseViewHolder viewHolder, WaresInfo wares) { SimpleDraweeView draweeView = (SimpleDraweeView) viewHolder.getView(R.id.drawee_view); draweeView.setImageURI(Uri.parse(wares.getImgUrl())); viewHolder.getTextView(R.id.text_title).setText(wares.getName()); }}
对比新建的 HWAdapter 和之前的 HotWaresAdapter,封装之后果然是不一样的。而在热门模块 HotFragment 中对于 HWAdapter 的调用和之前都是一样的,将 HotWaresAdapter 替换为 HWAdapter 就可以了,在 showData() 方法中修改,这里还是贴一下代码,修改之后的 showData() 方法如下。
private void showData() { switch (state) { case STATE_NORMAL: mAdatper = new HWAdapter(getActivity(), datas); recyclerView.setAdapter(mAdatper); recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.addItemDecoration(new WareItemDecoration(getContext(), WareItemDecoration.VERTICAL_LIST)); break; case STATE_REFREH: mAdatper.clearData(); mAdatper.addData(datas); recyclerView.scrollToPosition(0); layoutRefresh.finishRefresh(); break; case STATE_MORE: mAdatper.addData(mAdatper.getDatas().size(), datas); recyclerView.scrollToPosition(mAdatper.getDatas().size()); layoutRefresh.finishRefreshLoadMore(); break; } }
state 的三种模式分为正常状态、刷新状态以及加载更多的状态,处理和之前基本一样,只是替换为了新建的 HWAdapter。
2. 效果图
运行修改后的代码,效果图如下。
更多请关注公众号:Android技术学堂,会定期推送 Android 技术相关文章,谢谢支持。
- 商城项目实战 | 9.1 Adapter 封装的全面解析
- 商城之Adapter的封装
- 商城项目实战29:Httpclient封装的Http工具
- React Native商城项目实战04 - 封装TabNavigator.Item的创建
- 商城项目实战 | 6.2 OkHttp 轻松封装 更加灵活的调用
- 商城项目实战 | 4.1 RecyclerView 使用完全解析 体验艺术般的控件(一)
- 商城项目实战 | 4.2 RecyclerView 使用完全解析 体验艺术般的控件(二)
- 商城项目实战
- JNI 实战全面解析
- JNI 实战全面解析
- JNI 实战全面解析
- JNI 实战全面解析
- JNI 实战全面解析
- JNI 实战全面解析
- JNI 实战全面解析
- JNI 实战全面解析
- JNI 实战全面解析
- JNI 实战全面解析
- mac上hadoop伪分布式
- 个人笔记:关于java8 Lambda新特性
- T-SQL 列出所有表及列
- Web安全之XSS与SQL注入
- 解决导入项目目录太长导致的问题
- 商城项目实战 | 9.1 Adapter 封装的全面解析
- Python读写txt文本文件的操作方法全解析
- 【重大更新】DevExpress v17.1新版亮点(Windows 10篇)
- linux网络编程
- 关于MySQL的limit优化
- 后台启动kafka
- 开源
- 百度首页1,2,3面
- PHP学习笔记14:会话控制