我正在参与创作者训练营第6期,点击了解活动详情

前言

RecyclerView 的运用能够说是开发中必备的技能,咱们常常运用数据适配器RecyclerView.Adapter 的时分会觉得每次要写重复的代码,觉好麻烦。

一般咱们都是引进一些第三方库,或自己做一些简略的封装,这儿我共享一下自己的RV.Adapter的封装,运用装修器在不改变原有功用的状况下给RV.Adapter增加一些新的功用。

咱们能够比照一下自己封装,看看有没有什么启示,当然假如觉得我的封装不对或不好的当地也能够奉告我。

OK,那咱们开端吧!

一、对RV.Adapter的封装,增加数据处理

咱们自己起名封装一个基类 BaseRVAdapter 是承继 RecyclerView.Adapter 完成的,由所以承继完成的,所以严格来说它不是装修器,就是普通的经过承继扩展办法。

假如是装修器的界说,那么应该是把 RecyclerView.Adapter 当结构传入,再对它进行一些功用的扩展。

能不能运用装修器的办法来界说呢?当然是能够的,可是没有必要因为是基类的,有必要选择的,常用的逻辑,咱们为了便利其他人运用就用承继来完成有必要的逻辑。

话不多说直接上代码:

/**
 * RecyclerView的基类Adapter的封装。兼容多布局(根据data判别哪种布局)。
 * 能够兼容第三方RecyclerView
 */
public abstract class BaseRVAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> {
    protected int mLayoutId;
    protected List<T> mDatas;
    protected Context mContext;
    private IHasMoreType<T> mIMoreType;
    /**
     * 普通的数据填充走此布局
     */
    public BaseRVAdapter(Context context, List<T> datas, int layoutId) {
        mLayoutId = layoutId;
        mDatas = datas;
        mContext = context;
    }
    /**
     * 普通的数据填充走此布局,不参加数据源,有必要经过setDataList办法设置数据源
     */
    public BaseRVAdapter(Context context, int layoutId) {
        mLayoutId = layoutId;
        mContext = context;
    }
    /**
     * 设置数据源,只好在rv设置adapter之前调用
     */
    public void setDataList(List<T> datas) {
        mDatas = datas;
    }
    /**
     * 更新悉数的数据源
     *
     * @param datas 更新的悉数数据源
     */
    public void updateDataList(List<T> datas) {
        mDatas.clear();
        notifyDataSetChanged();
    }
    /**
     * 增加数据
     *
     * @param datas 需求增加的数据源
     */
    public void addDataList(List<T> datas) {
        if (mDatas != null) {
            mDatas.addAll(datas);
        }
        notifyItemRangeInserted(mDatas.size() - datas.size(), datas.size());
    }
    /**
     * 假如有多个布局走此结构办法
     */
    public BaseRVAdapter(Context context, List<T> datas, IHasMoreType<T> iMoreType) {
        mIMoreType = iMoreType;
        mDatas = datas;
        mContext = context;
    }
    /**
     * 多布局的获取布局类型
     */
    @Override
    public int getItemViewType(int position) {
        //假如支撑多布局
        if (mIMoreType != null) {
            return mIMoreType.getLayoutId(mDatas.get(position));
        }
        return super.getItemViewType(position);
    }
    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //假如支撑多布局
        if (mIMoreType != null) {
            mLayoutId = viewType;
        }
        return BaseViewHolder.get(mContext, parent, mLayoutId);
    }
    @Override
    public void onBindViewHolder(BaseViewHolder holder, int position) {
        /**  笼统办法交由子类具体完成  **/
        bindData(holder, mDatas.get(position), position);
    }
    public abstract void bindData(BaseViewHolder holder, T t, int position);
    @Override
    public int getItemCount() {
        return mDatas == null ? 0 : mDatas.size();
    }
}

多类型的布局接口操控:

/**
 * 假如有多布局的操控接口,能够传入结构办法
 */
public interface IHasMoreType<T> {
    //根据当时item的数据,回来指定的布局id
    int getLayoutId(T t);
}

还有一个 BaseViewHolder 我没有贴出来,是很简略的代码,就是对一些TextView CheckBos 等控件界说一些SetText Color ClickListener等办法。有需求到文末检查源码自取。

本功用由Java言语完成,更便利在Java与Kotlin两种言语中运用,办法都已经注释的很具体。

怎么运用与界说呢?简略的来看看:

界说数据与数据适配器,增加数据即可展现


    private val mDatas = mutableListOf<String>()
    private val mAdapter = JobAdapter(mDatas)
    override fun init() {
        mBinding.recyclerView.vertical().adapter = mAdapter
        initData()
    }
    private fun initData() {
        val newList = listOf("刘备", "关羽", "张飞", "赵云", "马超", "黄忠", "诸葛亮", "姜维", "王平", "魏延")
        mAdapter.addDataList(newList)
    }

简略的数据适配器:

class JobAdapter(datas: MutableList<String>) : BaseRVAdapter<String>(CommUtils.getContext(), datas, R.layout.item_custom_jobs) {
    override fun bindData(holder: BaseViewHolder, t: String?, position: Int) {
        holder.setText(R.id.tv_job_text, t)
    }
}

作用:

Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能

咱们能够很便利的完成一个根本的列表功用,假如是多布局的类型,咱们上面也进行了封装,完成咱们界说好的 IHasMoreType 接口,然后经过数据类型判别回来的布局id。在赋值的时分咱们根据数据类型对不同的布局做不同的绑定数据操作。

看看具体怎么运用:

    private val mDatas = mutableListOf<MoreJob>()
    private val mAdapter = Job2Adapter(mDatas)
    private fun initData() {
       mBinding.recyclerView.vertical().adapter = mAdapter
        val newList = listOf(
            MoreJob(1, "刘备"),
            MoreJob(1, "张飞"),
            MoreJob(2, "曹操"),
            MoreJob(1, "赵云"),
            MoreJob(2, "曹仁"),
            MoreJob(1, "马超"),
            MoreJob(1, "黄忠"),
            MoreJob(1, "诸葛亮"),
            MoreJob(2, "夏侯惇"),
            MoreJob(1, "姜维"),
            MoreJob(1, "王平"),
            MoreJob(2, "徐晃"),
            MoreJob(1, "魏延"),
            MoreJob(2, "张辽"),
            MoreJob(2, "曹真"),
            MoreJob(1, "刘禅"),
            MoreJob(1, "张苞"),
            MoreJob(2, "司马懿"),
        )
        mAdapter.addDataList(newList)
    }

Adapter的多布局运用:

class Job2Adapter(datas: MutableList<MoreJob>) : BaseRVAdapter<MoreJob>(CommUtils.getContext(), datas,
    IHasMoreType<MoreJob> {
        if (it.type == 1) return@IHasMoreType R.layout.item_custom_jobs
        else return@IHasMoreType R.layout.item_custom_image
    }) {
    override fun bindData(holder: BaseViewHolder, bean: MoreJob, position: Int) {
        if (bean.type == 1) {
            holder.setText(R.id.tv_job_text, bean.name)
        } else {
            holder.setText(R.id.tv_name, bean.name)
        }
    }
}

作用:

Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能

二、对RV.Adapter的装修,增加头脚布局

上文的 BaseRVAdapter 仅仅承继的完成,仅仅对数据处理,BaseViewHolder进行一些操作处理,并没有增加一些新的功用逻辑,仅仅对Adapter现有的功用进行整合与封装。

那么现在就不同了,咱们现在就是界说一个装修器,咱们把 RecyclerView.Adapter 当结构传入,对它进行一些功用的扩展。如增加/删去头布局,增加/删去脚布局等新的功用逻辑。

代码如下:(PS:为什么贴图不放源码?代码太多了不便利观看,其实代码是开源的,假如有需求能够去文末源码中自取。)

Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能

咱们对 RecyclerView.Adapter 现有的一些办法, onBindViewHolder getItemCount onCreateViewHolder 等办法做了一些处理,参加了一些头布局,脚布局的判别逻辑然后再调用 RecyclerView.Adapter 的处理。

其次,咱们还增加了一些全新的办法,如 addHeadView addFootView removeHeadView removeFootView 等办法,用于扩展它的功用。

因为运用了装修者形式,咱们直接把原来的 RecyclerView.Adapter 当结构传入,包裹起来就能完成新的功用。

    private val mDatas = mutableListOf<MoreJob>()
    private val mAdapter = Job2Adapter(mDatas)
    private val wrapAdapter = WrapRVAdapter(mAdapter)
      override fun init() {
        wrapAdapter.addHeadView(CommUtils.inflate(R.layout.item_vertal_header))
        wrapAdapter.addFootView(CommUtils.inflate(R.layout.item_vertal_fooder))
        mBinding.recyclerView.vertical().adapter = wrapAdapter
    }

增加数据的逻辑没变,仅仅把之前的 Adapter 包裹了一次,就能完成增加头布局脚布局的逻辑。

增加了头布局和脚布局之后,作用如下:

Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能

三、对RV.Adapter的装修,增加LoadMore功用

在日常的开发中,增加头布局和增加脚布局相对都是还算比较少见的,并且能够运用其他的办法完成,例如嵌套滑动,Appbarlayout等,所以增加头布局和脚布局是非有必要的。

可是一个列表的加载更多功用确是十分必要的,因为后端回来的接口是分页列表,咱们App需求配合做分页展现,也就需求LoadMore功用,能够说是绝大部分项目都会用到的。

往常运用的过程中,咱们需求滑动到底部增加LoadMore的回调,并露出一些办法,设置LoadMore的状况,比方Loading中,加载错误,加载成功,加载到底了。

信任咱们很多人都是运用的第三方库,又或者自己封装的,那接下来咱们就看看怎么运用装修器来封装一个加载更多的Adapter吧,对照一下有没有什么不一样。

首要咱们需求准备一个 LoadMoreView 对象,封装一些状况切换之类的工具办法。

然后咱们就能封装一个 LoadMoreAdapter 装修器,把 RecyclerView.Adapter 作为结构传参进去,然后对它现有办法进行一些改造,再增加一些新的办法扩展它的功用。

封装LoadMore的布局与状况切换:

/**
 * 自界说View 用于LoadMoreAdapter
 * RV中加载更多的布局
 */
public class LoadMoreView {
    public static final int STATUS_DEFAULT = 1;
    public static final int STATUS_LOADING = 2;
    public static final int STATUS_FAIL = 3;
    public static final int STATUS_END = 4;
    private int mLoadMoreStatus = STATUS_DEFAULT;
    //提供LoadMore的布局Id
    public int getLayoutId() {
        return R.layout.base_rv_loadmore_view;
    }
    public void setLoadMoreStatus(int loadMoreStatus) {
        this.mLoadMoreStatus = loadMoreStatus;
    }
    public int getLoadMoreStatus() {
        return mLoadMoreStatus;
    }
    public void showState(BaseViewHolder holder) {
        YYLogUtils.w("holder:" + holder + " mLoadMoreStatus:" + mLoadMoreStatus);
        if (mLoadMoreStatus == STATUS_DEFAULT) {
            visibleLoading(holder,false);
            visibleLoadFail(holder,false);
            visibleLoadEnd(holder,false);
        } else if (mLoadMoreStatus == STATUS_LOADING) {
            visibleLoading(holder,true);
            visibleLoadFail(holder,false);
            visibleLoadEnd(holder,false);
        } else if (mLoadMoreStatus == STATUS_FAIL) {
            visibleLoading(holder,false);
            visibleLoadFail(holder,true);
            visibleLoadEnd(holder,false);
        } else if (mLoadMoreStatus == STATUS_END) {
            visibleLoading(holder,false);
            visibleLoadFail(holder,false);
            visibleLoadEnd(holder,true);
        }
    }
    private void visibleLoading(BaseViewHolder holder, boolean visible) {
        holder.setVisible(R.id.load_more_loading_view, visible);
    }
    private void visibleLoadFail(BaseViewHolder holder, boolean visible) {
        holder.setVisible(R.id.load_more_load_fail_view, visible);
    }
    private void visibleLoadEnd(BaseViewHolder holder, boolean visible) {
        holder.setVisible(R.id.load_more_load_end_view, visible);
    }
}

加载更多的装修器:(PS:为什么贴图不放源码?代码太多了不便利观看,其实代码是开源的,假如有需求能够去文末源码中自取。)

Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能

注释的很具体,代码不难,咱们对LoadMore的完成有疑问的话能够谈论区提问。

运用:

    private val mDatas = mutableListOf<MoreJob>()
    private val mAdapter = Job2Adapter(mDatas)
    private val mLoadMoreAdapter = LoadMoreAdapter(mAdapter)
    override fun init() {
        mLoadMoreAdapter.isLoadMoreEnable = true
        mLoadMoreAdapter.setPreLoadNumber(3)
        mLoadMoreAdapter.setOnLoadMoreListener {
            loadMoreData()
        }
        mBinding.recyclerView.vertical().adapter = mLoadMoreAdapter
        initData()
    }
    private fun loadMoreData() {
        //模拟网络恳求
        CommUtils.getHandler().postDelayed({
            mAdapter.addDataList(
                listOf(
                    MoreJob(1, "孙权"),
                    MoreJob(1, "孙策"),
                    MoreJob(1, "孙尚香"),
                    MoreJob(1, "黄盖"),
                    MoreJob(1, "程普")
                )
            )
            mLoadMoreAdapter.loadMoreSuccess()
        }, 500)
    }
    private fun initData() {
        val newList = listOf(
            MoreJob(1, "刘备"),
            MoreJob(1, "张飞"),
            MoreJob(2, "曹操"),
            MoreJob(1, "赵云"),
            MoreJob(2, "曹仁"),
            MoreJob(1, "马超"),
            MoreJob(1, "黄忠"),
            MoreJob(1, "诸葛亮"),
            MoreJob(2, "夏侯惇"),
            MoreJob(1, "姜维"),
            MoreJob(1, "王平"),
            MoreJob(2, "徐晃"),
            MoreJob(1, "魏延"),
            MoreJob(2, "张辽"),
            MoreJob(2, "曹真"),
            MoreJob(1, "刘禅"),
            MoreJob(1, "张苞"),
            MoreJob(2, "司马懿"),
        )
        mAdapter.addDataList(newList)
    }

运转作用:

Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能

四、对RV.Adapter的装修,组合头脚布局和LoadMore的功用

其实按道理来说咱们两个装修器是能够一起运用的。

假如想一起用,那么能够把增加头布局的装修器当结构传入到加载更多的装修器中。这样加载更多的装修器就能够核算头布局和脚布局,才干得出正确的索引。装修器是能够嵌套套娃运用的。

可是现在Adapter的状况有一点特别,咱们需求处理真实Item的索引,当咱们增加了头布局,脚布局,又启用了LoadMore模块,那么真实Item的索引核算就有问题。

咱们一种做法就是把增加头布局脚布局的装修器作为结构传入到LoadMore的装修器中,就能够拿到头布局脚布局的数量,从而核算索引。

另一种做法就是把头布局脚布局的装修器和加载更多的装修器合并为一个装修器,这也是常用的一种方案。

具体的代码如下:(PS:为什么贴图不放源码?代码太多了不便利观看,其实代码是开源的,假如有需求能够去文末源码中自取。)

Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能
Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能

咱们有时分需求处理减去头布局的索引,有时分需求判别去除脚布局的索引,特别是判别当时索引是属于哪一个类型的时分,咱们还需求对当时索引做出判别。

运用起来咱们应该都会,这儿放一下运转的作用图:

Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能

总结

RV的一些封装是咱们开发项目必不可少的,有些人运用的是结构,有些人运用的自己的封装,一些结构封装的很好,功用很多,可是要注意看咱们是不是用得到这些功用,比方咱们的项目要求不高,只需求一些增加头布局,脚布局等根本功用的RV,咱们完全能够运用自己的封装,更加的灵敏,便利修正。

假如当时场景仅仅一个普通的列表展现,那么咱们就只需求普通的 BaseRVAdapter ,假如当然的场景是需求加载更多的RV列表或增加头部的功用,咱们也能够运用上述的装修器来包装 BaseRVAdapter ,从而灵敏的完成咱们的需求。

最终阐明一下,本文的代码有一点多,因为都是重要代码,需求悉数贴出来才干展现逻辑,尽管如此仍是有一些不重要的代码没有贴出来,一些布局文件没有贴出来,假如咱们有兴趣能够检查源码自取。本文悉数代码都在里边。

好了,本期内容如讲的不到位或错漏的当地,期望同学们能够指出交流。

假如感觉本文对你有一点点点的启示,还望你能点赞支撑一下,你的支撑是我最大的动力。

Ok,这一期就此结束。

Android设计模式实战-对RV.Adapter进行装饰,实现addHead与LoadMore功能