商城项目实战 | 9.1 Adapter 封装的全面解析

本文为菜鸟窝作者刘婷的连载。”商城项目实战”系列来聊聊仿”京东淘宝的购物商城”如何实现。

在之前的文章《商城项目实战 | 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 views;

    public BaseViewHolder(View itemView){
        super(itemView);
        this.views = new SparseArray();
    }

    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 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 datas;

    public BaseAdapter(Context context, int layoutResId) {
        this(context, layoutResId, null);
    }

    public BaseAdapter(Context context, int layoutResId, List datas) {
        this.datas = datas == null ? new ArrayList() : 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();
    }

   @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 getDatas() {

        return datas;
    }

    public void addData(List datas) {
        addData(0, datas);
    }

    public void addData(int position, List 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 datas) {
        super(context, layoutResId, datas);
    }
}

之后新增的 Adapter 都继承于 SimpleAdapter 就可以了,更为的方便了。

使用封装好的 Adapter

在文章《商城项目实战 | 8.2 SwipeRefreshLayout 实现可以下拉刷新和加载更多的热门商品列表》中实现了可以下拉刷新和加载更多的热门商品列表,相应列表的 Adapter 的实现写得比较繁琐,现在就通过使用封装好的 Adapter 来使代码简单起来。

1. 实现热门商品列表的 Adapter

之前在里面定义的列表 Adapter 的代码如下。

public class HotWaresAdapter  extends RecyclerView.Adapter  {

    private List mDatas;

    private LayoutInflater mInflater;

    public HotWaresAdapter(List 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 getDatas(){

        return  mDatas;
    }
    public void clearData(){

        mDatas.clear();
        notifyItemRangeRemoved(0,mDatas.size());
    }

    public void addData(List datas){

        addData(0,datas);
    }

    public void addData(int position,List 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 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 封装的全面解析_第1张图片

你可能感兴趣的:(商城项目实战,商城,框架,adapter,封装,京东)