美文网首页
RecyclerView通用包装类-增加底部footer和上拉加

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

作者: XJ_crazy | 来源:发表于2019-01-12 13:33 被阅读0次

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

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

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

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

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

          本文链接:https://www.haomeiwen.com/subject/vfaqdqtx.html