RecyclerView.Adapter适配器通用化改造

RecyclerView 是谷歌给开发者的福利,比以往的ListView更加强大,性能更大,具体源码分析,我们下次讲。本篇主要介绍如何在项目中提炼代码,增强它的适配性。下面就介绍我的思路。

首先一般编码有如下几个问题。
1.列表数据,需要写一个RecyclerView.Adapter和一个ViewHolder。ViewHolder是根据不同布局而来,但RecyclerView.Adapter则包含大量的重复代码是否可以只对修改编程?

2.项目中常见的是列表嵌套列表,数据的传递,在子列表适配器,或者孙子,重孙适配器中编码的复杂度是否有好的解决方案?

下面贴出我的方案代码

public class MyCommonAdapter<T> extends RecyclerView.Adapter implements FindFather<MyCommonAdapter.ApaterParent> {

    ApaterParent apaterParent = null;

    @Override
    public ApaterParent getFather() {
        return apaterParent;
    }

    @Override
    public void admitFather(ApaterParent father) {
        this.apaterParent = father;

    }


    public static abstract class ApaterParent<T> {


        public ApaterParent father;

        public abstract int getPosition();

        public abstract T getBean();

        public ApaterParent getFather() {
            return father;
        }

        public void setFather(ApaterParent father) {
            this.father = father;
        }
    }

    public static final String TAG = MyCommonAdapter.class.getName();
    /**
     * 列表数据集合.
     */
    protected List<T> dataList = new ArrayList<>();
    /**
     * 布局填充.
     */
    protected LayoutInflater mInflater;

    /**
     * 一个adapter 适配一个viewholder.
     */
    private Class<T> viewHolder;


    /**
     * M context.
     */
    protected Context mContext;

    private Fragment fragment;

    public MyCommonAdapter(Context mContext, Class<T> viewHolder) {
        this.viewHolder = viewHolder;
        this.mContext = mContext;
        mInflater = LayoutInflater.from(mContext);
    }


    public MyCommonAdapter(Fragment fragment, Class<T> viewHolder) {
        this(fragment.getActivity(), viewHolder);
        this.fragment = fragment;

    }


    @NonNull
    @Override
    public MyCommonAdapter.DefineHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        UseLayout layout = viewHolder.getAnnotation(UseLayout.class);
        if (layout == null) {
            throw new RuntimeException("请设置布局注解");
        }
        int layoutRes = 0;
        if (layout != null) {
            layoutRes = layout.layoutRes();
        }
        try {
            Constructor<T> constructor = viewHolder.getConstructor(Context.class, View.class);
            MyCommonAdapter.DefineHolder viewHolder = (MyCommonAdapter.DefineHolder) constructor.newInstance(mContext, mInflater.inflate(layoutRes, null, false));
            viewHolder.admitFather(apaterParent);
            viewHolder.setAdapter(this);
            if (fragment != null) {
                viewHolder.setFragment(fragment);
            }
            return viewHolder;
        } catch (Exception e) {
            LogUtils.d(TAG, "onCreateViewHolder 发生错误:", e.toString());
        }
        return null;
    }

    @Override
    public void onBindViewHolder(final @NonNull RecyclerView.ViewHolder holder, final int position) {
        if (holder instanceof MyCommonAdapter.DefineHolder) {
            ((MyCommonAdapter.DefineHolder) holder).setCount(dataList.size());
            final T itemBean = dataList.get(position);
            if (itemBean != null) {
                MyCommonAdapter.ApaterParent<T> myself = new MyCommonAdapter.ApaterParent<T>() {

                    @Override
                    public int getPosition() {
                        return position;
                    }

                    @Override
                    public T getBean() {
                        return itemBean;
                    }
                };
                myself.setFather(getFather());
                ((MyCommonAdapter.DefineHolder) holder).setData(itemBean, position, myself);

            }
        }

    }

    public static abstract class DefineHolder<T> extends RecyclerView.ViewHolder implements  FindFather<ApaterParent> {


        private RecyclerView.Adapter adapter;
        ApaterParent parent = null;

        private Fragment fragment;
        private Context context;
        private int count;

        public DefineHolder(Context context, View itemView) {
            super(itemView);
            this.context = context;
            ButterKnife.bind(this, itemView);


        }

        public RecyclerView.Adapter getAdapter() {
            return adapter;
        }

        public void setAdapter(RecyclerView.Adapter adapter) {
            this.adapter = adapter;
        }


        public void notifyDataSetChanged() {

            if (adapter != null) {
                adapter.notifyDataSetChanged();
            }
        }


        @Override
        public void admitFather(ApaterParent father) {
            parent = father;
        }

        @Override
        public ApaterParent getFather() {
            return parent;
        }


        public boolean isLastPosition() {
            return count - 1 == getLayoutPosition();
        }

        public boolean isFirstPosition() {
            return 0 == getLayoutPosition();

        }


        public int getCount() {
            return count;
        }

        public void setCount(int count) {
            this.count = count;
        }

        public Fragment getFragment() {
            return fragment;
        }

        public void setFragment(Fragment fragment) {
            this.fragment = fragment;
        }

        public Context getContext() {
            return context;
        }

        public void setContext(Context context) {
            this.context = context;
        }

        public abstract void setData(final T bean, final int position, final ApaterParent myself);

    }


    public boolean isEmpty() {

        return dataList.isEmpty();
    }

    @Override
    public int getItemCount() {
        return dataList.size();
    }

    public void updateData(List<T> data, boolean isClearOld) {
        if (isClearOld) {
            dataList.clear();
        }
        if (data != null && !data.isEmpty()) {
            dataList.addAll(data);
        }
        notifyDataSetChanged();

    }


}

通用适配器类继承RecyclerView.Adapter 实现FindFather<MyCommonAdapter.ApaterParent>。
首先介绍下一些常见的抽象。

列表的数据集合

//列表的数据集合
    protected List<T> dataList = new ArrayList<>();

viewHodler封装易变的布局和数据与控件的代码编写

   private Class<T> viewHolder;

保留Activity与Fragment,在构造函数中重载了两种类型,按需使用

   protected Context mContext;
   private Fragment fragment;

常规操作判断空,获取数据集大小,更新列表数据

  public boolean isEmpty() {

        return dataList.isEmpty();
    }

    @Override
    public int getItemCount() {
        return dataList.size();
    }

    public void updateData(List<T> data, boolean isClearOld) {
        if (isClearOld) {
            dataList.clear();
        }
        if (data != null && !data.isEmpty()) {
            dataList.addAll(data);
        }
        notifyDataSetChanged();

    }

定义一个抽象类DefineHolder,这个类是需要自己继承实现的,是我们完成业务的编程空间

    public static abstract class DefineHolder<T> extends RecyclerView.ViewHolder implements  FindFather<ApaterParent> {

我们以前类似于getView的代码都写在此处

        public abstract void setData(final T bean, final int position, final ApaterParent myself);

常规代码不做解释

  public void notifyDataSetChanged() {

            if (adapter != null) {
                adapter.notifyDataSetChanged();
            }
        }
        public boolean isLastPosition() {
            return count - 1 == getLayoutPosition();
        }
        public boolean isFirstPosition() {
            return 0 == getLayoutPosition();

        }

ApaterParent是用于解决嵌套adapter中的数据,位置信息的传递,可以想象成一个子到父的链表,通过getFather方法可以取到自己的父adapter,可以依次逐级调用,getBean,getPosition可以取到需要的数据和位置

 public static abstract class ApaterParent<T> {


        public ApaterParent father;

        public abstract int getPosition();

        public abstract T getBean();

        public ApaterParent getFather() {
            return father;
        }

        public void setFather(ApaterParent father) {
            this.father = father;
        }
    }

onCreateViewHolder 这个方法中可以看到首先根据运行时注解UseLayout取到对应的布局,随后通过反射创建一个MyCommonAdapter.DefineHolder实例,实例链接到父类适配器,返回


 @NonNull
    @Override
    public MyCommonAdapter.DefineHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        UseLayout layout = viewHolder.getAnnotation(UseLayout.class);
        if (layout == null) {
            throw new RuntimeException("请设置布局注解");
        }
        int layoutRes = 0;
        if (layout != null) {
            layoutRes = layout.layoutRes();
        }
        try {
            Constructor<T> constructor = viewHolder.getConstructor(Context.class, View.class);
            MyCommonAdapter.DefineHolder viewHolder = (MyCommonAdapter.DefineHolder) constructor.newInstance(mContext, mInflater.inflate(layoutRes, null, false));
            viewHolder.admitFather(apaterParent);
            viewHolder.setAdapter(this);
            if (fragment != null) {
                viewHolder.setFragment(fragment);
            }
            return viewHolder;
        } catch (Exception e) {
            LogUtils.d(TAG, "onCreateViewHolder 发生错误:", e.toString());
        }
        return null;
    }

onBindViewHolder 负责创建MyCommonAdapter.ApaterParent节点传入并且将数据和位置传给实现的setData方法。

  @Override
    public void onBindViewHolder(final @NonNull RecyclerView.ViewHolder holder, final int position) {
        if (holder instanceof MyCommonAdapter.DefineHolder) {
            ((MyCommonAdapter.DefineHolder) holder).setCount(dataList.size());
            final T itemBean = dataList.get(position);
            if (itemBean != null) {
                MyCommonAdapter.ApaterParent<T> myself = new MyCommonAdapter.ApaterParent<T>() {

                    @Override
                    public int getPosition() {
                        return position;
                    }

                    @Override
                    public T getBean() {
                        return itemBean;
                    }
                };
                myself(getFather());
                ((MyCommonAdapter.DefineHolder) holder).setData(itemBean, position, myself);

            }
        }

    }

现在需要展示一个九宫格的图片列表。下面是示范代码。MyCommonAdapter构造函数,第一个参数,Fragment或者Activity, 第二个参数自己定义的业务ViewHolder,admitFather方法绑定父类(这段代码也是在一个adapter中,单层adapter不需要认爹)。 其他的就是常见代码

  List<AlongSceneryDetailBean.DetailListBean> detailList = bean.getDetailList();
        if (detailList != null && !detailList.isEmpty()) {
            MyCommonAdapter imageAdapter = new MyCommonAdapter(getFragment(), AlongSceneryImageItemViewHolder.class);
            imageAdapter.admitFather(myself);
            GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(),3);
            dataRv.setLayoutManager(gridLayoutManager);
            dataRv.setAdapter(imageAdapter);
            imageAdapter.updateData(detailList, true);

        } else {
            dataRv.setVisibility(View.GONE);
        }

UseLayout注解中设置layout,设置业务数据AlongSceneryDetailBean.DetailListBean, ButterKnife绑定控件,setData中写业务逻辑

@UseLayout(layoutRes = R.layout.item_alone_scenery_image)
public class AlongSceneryImageItemViewHolder extends MyCommonAdapter.DefineHolder<AlongSceneryDetailBean.DetailListBean> {


    @Bind(R.id.content_iv)
    ImageView contentIv;

    public AlongSceneryImageItemViewHolder(Context context, View itemView) {
        super(context, itemView);

    }

    @Override
    public void setData(final AlongSceneryDetailBean.DetailListBean bean, int position,final MyCommonAdapter.ApaterParent myself) {
        if(!TextUtils.isEmpty(bean.getImgUrl())){
            Glide.with(getContext()).load(bean.getImgUrl()).into(contentIv);
        }else{
            Glide.with(getContext()).load(R.color.white_pure).into(contentIv);
        }
        contentIv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
           
				//do something
                        }
                    }

                }

            }
        });



    }
}

UseLayout注解中设置layout,设置业务数据AlongSceneryDetailBean.DetailListBean, ButterKnife绑定控件,setData中写业务逻辑

 MyCommonAdapter.ApaterParent<AlongSceneryDetailBean.MileageSceneryList>  dataApaterParent = getFather();
        if(dataApaterParent!=null){
             mileageSceneryList =   dataApaterParent.getBean();
             //取得父adapter 中的bean,获取父类位置。做你想做的事

            }
        }

一个通用的适配器就算完成,项目中还是要多抽象,多复用,这样对于机械的任务,编码越来越轻松,且不易出错,可以有更多时间挑战有难度的事情。

猜你喜欢

转载自blog.csdn.net/atxxiang4/article/details/85044212