RecyclerView通用包裝類-增加底部footer和上拉加載更多

在這里我講一下如何寫一個通用的工具類來增加RecyclerView的底部footer和上拉加載更多的方式

該包裝類的實現(xiàn)思路是對自定義的adapter進行外部攔截棉安,重寫Adapter的必要方法來實現(xiàn)上述功能。

功能實現(xiàn)簡潔易懂,主要是我覺得思想不錯盔性,可以借鑒鼓鲁。

首先看傳入參數(shù)戒努,了解類的大致構造:

    context: Context, //該上下文是為了獲取inflater實例
    val mAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>, //傳入自定義adapter的引用是為了方便調用內部屬性與方法
    val initialPageCount: Int, //初始化每一頁要加載的數(shù)據量
    val recycler: RecyclerView) //傳入recyclerview的id是為了為其添加監(jiān)聽,回調加載更多接口料祠,更改footer狀態(tài)

接下來重寫四個必要方法(注釋都寫在代碼里面,很詳細):
1.getItemCount

    /**
     * 加1是因為包裝類為自定義的adapter增加了一個footer
     */
    override fun getItemCount(): Int  = mAdapter.itemCount + 1

2.getItemViewType

    override fun getItemViewType(position: Int): Int {
        //如果首次加載的數(shù)據少于 initialPageCount 行澎羞,則表明沒有更多數(shù)據了髓绽,直接返回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.接下來就是增加監(jiān)聽了株茶,這時候就是傳入recyclerview id的用處了
為列表增加滑動監(jiān)聽来涨,重寫onScrolled和onScrollStateChanged
在onScrolled內部獲取到顯示的最后一個item的position
在onScrollStateChanged內部進行三個判斷,1.判斷是否滑動已經停止启盛,2判斷footer是否是loading狀態(tài)蹦掐,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()
                    }
                //這里現(xiàn)在只寫了LinearLayoutManager的代碼僵闯,后續(xù)補充
                }
            }
            override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
                if (newState == RecyclerView.SCROLL_STATE_IDLE //停止?jié)L動
                        && footerType == TYPE_FOOTER_LOADING  //footer狀態(tài)為加載更多
                        && lastVisibleItem >= mAdapter.itemCount)  //最后一個顯示的item序號大于適配器item數(shù)量卧抗,即顯示了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() {
                            //在這里監(jiān)聽到加載更多回調
                        }
                    })
activity_course_search_recycler.adapter = courseSearchAdapterWrapper

下面是類的源碼

/**
 * @author <a href="http://www.reibang.com/u/c1e5310dd724">xujian</a>
 * @描述: 課程搜索頁面列表適配器包裝類,包裝了底部item(加載中和到達底部item的變化鳖粟,還有上拉到底部的監(jiān)聽)
 * @Copyright Copyright (c) 2019
 * @Company ***
 * @date 2019/01/07
 */
class CourseSearchAdapterWrapper(
        context: Context, //該上下文是為了獲取inflater實例
        val mAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>, //傳入自定義adapter的引用是為了方便調用內部屬性與方法
        val initialPageCount: Int, //初始化每一頁要加載的數(shù)據量
        val recycler: RecyclerView) //傳入recyclerview的id是為了為其添加監(jiān)聽社裆,回調加載更多接口,更改footer狀態(tài)
    : 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 //底部無更多數(shù)據view
    }

    /**
     * 加1是因為包裝類為自定義的adapter增加了一個footer
     */
    override fun getItemCount(): Int  = mAdapter.itemCount + 1

    override fun getItemViewType(position: Int): Int {
        //如果首次加載的數(shù)據少于 initialPageCount 行向图,則表明沒有更多數(shù)據了泳秀,直接返回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)
            }
        }
    }

    //需要加載更多數(shù)據時調用
    fun insertData() {
        footerType = TYPE_FOOTER_LOADING
        notifyDataSetChanged()
    }

    //沒有更多數(shù)據時候調用
    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()
                    }
                //這里現(xiàn)在只寫了LinearLayoutManager的代碼磺陡,后續(xù)補充
                }
            }
            override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
                if (newState == RecyclerView.SCROLL_STATE_IDLE //停止?jié)L動
                        && footerType == TYPE_FOOTER_LOADING  //footer狀態(tài)為加載更多
                        && lastVisibleItem >= mAdapter.itemCount)  //最后一個顯示的item序號大于適配器item數(shù)量,即顯示了footer
                    onLoadMoreListener?.onLoadMore()
            }
        })
    }

    fun setOnLoadMoreListener (listener: OnLoadMoreListener): CourseSearchAdapterWrapper {
        this.onLoadMoreListener = listener
        //設置滑動監(jiān)聽
        setOnScrollerListener()
        return this
    }

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

    /**
     * 自定義的底部item漠畜,后續(xù)會繼續(xù)暴露方法給外部動態(tài)設置加載更多時候的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 = "已經到達底部"
                }
            }
        }
    }

}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末币他,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子憔狞,更是在濱河造成了極大的恐慌蝴悉,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瘾敢,死亡現(xiàn)場離奇詭異拍冠,居然都是意外死亡,警方通過查閱死者的電腦和手機簇抵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門庆杜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人碟摆,你說我怎么就攤上這事晃财。” “怎么了典蜕?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵断盛,是天一觀的道長罗洗。 經常有香客問我,道長钢猛,這世上最難降的妖魔是什么伙菜? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮厢洞,結果婚禮上仇让,老公的妹妹穿的比我還像新娘。我一直安慰自己躺翻,他們只是感情好丧叽,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著公你,像睡著了一般踊淳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上陕靠,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天迂尝,我揣著相機與錄音,去河邊找鬼剪芥。 笑死垄开,一個胖子當著我的面吹牛,可吹牛的內容都是我干的税肪。 我是一名探鬼主播溉躲,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼益兄!你這毒婦竟也來了锻梳?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤净捅,失蹤者是張志新(化名)和其女友劉穎疑枯,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛔六,經...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡荆永,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了国章。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屁魏。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖捉腥,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情你画,我是刑警寧澤抵碟,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布桃漾,位于F島的核電站,受9級特大地震影響拟逮,放射性物質發(fā)生泄漏撬统。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一敦迄、第九天 我趴在偏房一處隱蔽的房頂上張望恋追。 院中可真熱鬧,春花似錦罚屋、人聲如沸苦囱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撕彤。三九已至,卻和暖如春猛拴,著一層夾襖步出監(jiān)牢的瞬間羹铅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工愉昆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留职员,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓跛溉,卻偏偏與公主長得像焊切,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子倒谷,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內容

  • 判斷是否滑動到最后 / 前的 Item 添加 HeaderView 和 FooterView ScrollView...
    chauI閱讀 1,570評論 0 1
  • 又到了更新博文的時間了蛛蒙,最近在看一本很不錯的心理學書籍,名字叫做 《拖延心理學》渤愁,封面長下面這樣子 書的內容主要是...
    ec95b5891948閱讀 57,447評論 38 472
  • 很多時候牵祟,項目中都會有列表加載更多的場景,這次我們讓RecyclerView輕松擁有加載更多的功能抖格。雖然已有許多類...
    SheHuan閱讀 28,741評論 45 184
  • 想把網頁上的一些效果改成Android上的诺苹,就遇到一個列表的上拉加載和下拉刷新,Android只提供了下拉加載更多...
    俄城WESTBOOKMVP閱讀 1,056評論 0 0
  • 累到瘋雹拄。有人知道么收奔?沒有。 累到瘋有人關心么滓玖?沒有坪哄。 累到瘋有什么?只有自己身心俱疲繼續(xù)走。
    無名的豆撈閱讀 159評論 0 0