在這里我講一下如何寫一個通用的工具類來增加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 = "已經到達底部"
}
}
}
}
}