RecyclerView通用包装类-增加底部footer和上拉加载更多

在这里我讲一下如何写一个通用的工具类来增加RecyclerView的底部footer和上拉加载更多的方式

该包装类的实现思路是对自定义的adapter进行外部拦截,重写Adapter的必要方法来实现上述功能。

功能实现简洁易懂,主要是我觉得思想不错,可以借鉴。

首先看传入参数,了解类的大致构造:

    context: Context, //该上下文是为了获取inflater实例
    val mAdapter: RecyclerView.Adapter, //传入自定义adapter的引用是为了方便调用内部属性与方法
    val initialPageCount: Int, //初始化每一页要加载的数据量
    val recycler: RecyclerView) //传入recyclerview的id是为了为其添加监听,回调加载更多接口,更改footer状态

接下来重写四个必要方法(注释都写在代码里面,很详细):
1.getItemCount

    /**
     * 加1是因为包装类为自定义的adapter增加了一个footer
     */
    override fun getItemCount(): Int  = mAdapter.itemCount + 1

2.getItemViewType

    override fun getItemViewType(position: Int): Int {
        //如果首次加载的数据少于 initialPageCount 行,则表明没有更多数据了,直接返回TYPE_FOOTER_NOMORE类型footer
        if (position == mAdapter.itemCount && mAdapter.itemCount < initialPageCount) return TYPE_FOOTER_NOMORE
        return when (position < mAdapter.itemCount) {
            // 0-mAdapter.itemCount-1,调用自定义adapter内的getItemViewType 
            true -> mAdapter.getItemViewType(position)
            // 第mAdapter.getItemViewType个,是包装类内部定义的,返回footer的ViewType类型值
            false -> footerType!!
        }
    }

3.onCreateViewHolder

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder {
        var view:View? = null
        return when (viewType) {
            //TYPE_FOOTER_LOADING是加载更多footer的ViewType类型值
            TYPE_FOOTER_LOADING -> {
                FooterHolder(mInflater!!.inflate(R.layout.footer, parent, false), TYPE_FOOTER_LOADING)
            }
            //TYPE_FOOTER_NOMORE是到达底部footer的ViewType类型值
            TYPE_FOOTER_NOMORE -> {
                FooterHolder(mInflater!!.inflate(R.layout.footer, parent, false), TYPE_FOOTER_NOMORE)
            }
            //其余情况均返回自定义adapter内部的onCreateViewHolder
            else -> {
                mAdapter.onCreateViewHolder(parent, viewType)
            }
        }
    }

4.onBindViewHolder

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {
        when (holder) {
            //如果是footer,那么自己处理,不然就调用自定义adapter内部的onBindViewHolder
            is FooterHolder -> { }
            else -> {
                mAdapter.onBindViewHolder(holder, position)
            }
        }
    }

5.接下来就是增加监听了,这时候就是传入recyclerview id的用处了
为列表增加滑动监听,重写onScrolled和onScrollStateChanged
在onScrolled内部获取到显示的最后一个item的position
在onScrollStateChanged内部进行三个判断,1.判断是否滑动已经停止,2判断footer是否是loading状态,3.判断最后一个item是否是footer,全部满足才进行回调加载更多接口

    fun setOnScrollerListener() {
        recycler.addOnScrollListener(object: RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
                val layoutManager = recyclerView?.layoutManager

                when (layoutManager) {
                    is LinearLayoutManager -> {
                        lastVisibleItem = layoutManager.findLastVisibleItemPosition()
                    }
                //这里现在只写了LinearLayoutManager的代码,后续补充
                }
            }
            override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
                if (newState == RecyclerView.SCROLL_STATE_IDLE //停止滚动
                        && footerType == TYPE_FOOTER_LOADING  //footer状态为加载更多
                        && lastVisibleItem >= mAdapter.itemCount)  //最后一个显示的item序号大于适配器item数量,即显示了footer
                    onLoadMoreListener?.onLoadMore()
            }
        })
    }

使用的时候可以像下面这样使用:

//初始化自定义adapter
courseSearchAdapter = CourseSearchAdapter(this, CourseSearchActicity@this,courseList!!)
activity_course_search_recycler.layoutManager = LinearLayoutManager(this)
 courseSearchAdapterWrapper = CourseSearchAdapterWrapper(
                    this,
                    courseSearchAdapter!!,
                    20,
                    activity_course_search_recycler)
                    .setOnLoadMoreListener(object : CourseSearchAdapterWrapper.OnLoadMoreListener {
                        override fun onLoadMore() {
                            //在这里监听到加载更多回调
                        }
                    })
activity_course_search_recycler.adapter = courseSearchAdapterWrapper

下面是类的源码

/**
 * @author xujian
 * @描述: 课程搜索页面列表适配器包装类,包装了底部item(加载中和到达底部item的变化,还有上拉到底部的监听)
 * @Copyright Copyright (c) 2019
 * @Company ***
 * @date 2019/01/07
 */
class CourseSearchAdapterWrapper(
        context: Context, //该上下文是为了获取inflater实例
        val mAdapter: RecyclerView.Adapter, //传入自定义adapter的引用是为了方便调用内部属性与方法
        val initialPageCount: Int, //初始化每一页要加载的数据量
        val recycler: RecyclerView) //传入recyclerview的id是为了为其添加监听,回调加载更多接口,更改footer状态
    : RecyclerView.Adapter() {

    private var mInflater: LayoutInflater? = null
    private var footerType = TYPE_FOOTER_LOADING //默认为正在加载
    private var lastVisibleItem:Int = 0
    private var onLoadMoreListener: OnLoadMoreListener? = null

    init {
        mInflater = LayoutInflater.from(context)
    }

    companion object {
        const val TYPE_FOOTER_LOADING = 100000001 //底部正在加载view
        const val TYPE_FOOTER_NOMORE = 100000002 //底部无更多数据view
    }

    /**
     * 加1是因为包装类为自定义的adapter增加了一个footer
     */
    override fun getItemCount(): Int  = mAdapter.itemCount + 1

    override fun getItemViewType(position: Int): Int {
        //如果首次加载的数据少于 initialPageCount 行,则表明没有更多数据了,直接返回TYPE_FOOTER_NOMORE类型footer
        if (position == mAdapter.itemCount && mAdapter.itemCount < initialPageCount) return TYPE_FOOTER_NOMORE
        return when (position < mAdapter.itemCount) {
            // 0-mAdapter.itemCount-1,调用自定义adapter内的getItemViewType 
            true -> mAdapter.getItemViewType(position)
            // 第mAdapter.getItemViewType个,是包装类内部定义的,返回footer的ViewType类型值
            false -> footerType!!
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder {
        var view:View? = null
        return when (viewType) {
            //TYPE_FOOTER_LOADING是加载更多footer的ViewType类型值
            TYPE_FOOTER_LOADING -> {
                FooterHolder(mInflater!!.inflate(R.layout.footer, parent, false), TYPE_FOOTER_LOADING)
            }
            //TYPE_FOOTER_NOMORE是到达底部footer的ViewType类型值
            TYPE_FOOTER_NOMORE -> {
                FooterHolder(mInflater!!.inflate(R.layout.footer, parent, false), TYPE_FOOTER_NOMORE)
            }
            //其余情况均返回自定义adapter内部的onCreateViewHolder
            else -> {
                mAdapter.onCreateViewHolder(parent, viewType)
            }
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {
        when (holder) {
            //如果是footer,那么自己处理,不然就调用自定义adapter内部的onBindViewHolder
            is FooterHolder -> { }
            else -> {
                mAdapter.onBindViewHolder(holder, position)
            }
        }
    }

    //需要加载更多数据时调用
    fun insertData() {
        footerType = TYPE_FOOTER_LOADING
        notifyDataSetChanged()
    }

    //没有更多数据时候调用
    fun noMoreData() {
        footerType = TYPE_FOOTER_NOMORE
        notifyItemChanged(mAdapter.itemCount)
    }

    fun setOnScrollerListener() {
        recycler.addOnScrollListener(object: RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
                val layoutManager = recyclerView?.layoutManager

                when (layoutManager) {
                    is LinearLayoutManager -> {
                        lastVisibleItem = layoutManager.findLastVisibleItemPosition()
                    }
                //这里现在只写了LinearLayoutManager的代码,后续补充
                }
            }
            override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
                if (newState == RecyclerView.SCROLL_STATE_IDLE //停止滚动
                        && footerType == TYPE_FOOTER_LOADING  //footer状态为加载更多
                        && lastVisibleItem >= mAdapter.itemCount)  //最后一个显示的item序号大于适配器item数量,即显示了footer
                    onLoadMoreListener?.onLoadMore()
            }
        })
    }

    fun setOnLoadMoreListener (listener: OnLoadMoreListener): CourseSearchAdapterWrapper {
        this.onLoadMoreListener = listener
        //设置滑动监听
        setOnScrollerListener()
        return this
    }

    interface OnLoadMoreListener {
        fun onLoadMore() //加载更多
    }

    /**
     * 自定义的底部item,后续会继续暴露方法给外部动态设置加载更多时候的gif图
     */
    class FooterHolder(view: View, type:Int): RecyclerView.ViewHolder(view) {
        var txt: TextView? = null

        init {
            txt = view.findViewById(R.id.footer_txt)
            when (type) {
                TYPE_FOOTER_LOADING -> {
                    txt!!.text = "正在加载..."
                }
                TYPE_FOOTER_NOMORE -> {
                    txt!!.text = "已经到达底部"
                }
            }
        }
    }

}

你可能感兴趣的:(RecyclerView通用包装类-增加底部footer和上拉加载更多)