在这里我讲一下如何写一个通用的工具类来增加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 = "已经到达底部"
}
}
}
}
}