旧机宝的用户反馈、设备列表、功能列表等等都有可能存在大量数据,需要进行分页显示,一个下拉刷新上划加载更多的控件是非常有必要的。这个时候就想到了原生的下拉刷新控件SwipeRefreshLayout,原生的应该支持最好也最简单纯粹,对于上划加载更多则通过RecyclerView的滑动坚挺来实现,之所以选用RecyclerView是因为其支持非常广泛的布局方式,有取代一众listview的趋势。为了方便服用,直接封装为一个新的控件。
第一步:一个通知程序何时刷新、加载更多数据的接口
通过将该接口传给adapter来实现刷新、加载更多数据的回调操作,接口代码如下
interface OnLoadDataListener {
fun refresh()
fun loadMore()
}
第二步:一个嵌套RecyclerView的SwipeRefreshLayout控件
新建一个类RefreshLayout,使其继承自SwipeRefreshLayout,因而具备SwipeRefreshLayout的下拉刷新能力,通过setOnRefreshListener把刷新事件通过接口OnLoadDataListener回调出去:
setOnRefreshListener {
if (onLoadDataListener != null) {
isRefreshing = true
onLoadDataListener.refresh()
} else {
isRefreshing = false
}
}
在其实例化的时候,为其加载一个RecyclerView,借此实现数据容器和上划加载更多的功能。通过addOnScrollListener的方式把上划加载更多的事件回调出去,当RecyclerView的最后一项被显示出来的时候,执行加载更多回调:
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
var lastVisiablePosition = 0
val layoutManager = recyclerView.getLayoutManager()
if (layoutManager is LinearLayoutManager) {
lastVisiablePosition = layoutManager
.findLastVisibleItemPosition()
} else if (layoutManager is GridLayoutManager) {
lastVisiablePosition = layoutManager
.findLastVisibleItemPosition()
} else if (layoutManager is StaggeredGridLayoutManager) {
val into = IntArray(
layoutManager
.getSpanCount()
)
layoutManager.findLastVisibleItemPositions(into)
lastVisiablePosition = findMax(into)
}
}
val adapter:Adapter= recyclerView.adapter as Adapter
if (lastVisiablePosition == adapter.myGetItemCount()) {
if (onLoadDataListener != null && !isRefreshing()) {
adapter.showLoading()
onLoadDataListener.loadMore()
}
}
}
}
})
RecyclerView通过layoutmanager来指定数据的显示方式(几行几列横向竖向),大部分情况下用的是竖向列表布局,因而可以为recyclerView设置一个默认的LinearLayoutManager,不过也要提供方法setLayoutManager来提供个性化的能力,最后,要有一个setAdapter方法来设置数据适配器,就像RecyclerView一样。
为
fun setLayoutManager(layoutManager: RecyclerView.LayoutManager) {
recyclerView.layoutManager = layoutManager
}
fun setAdapter(adapter: Adapter) {
mAdapter=adapter
recyclerView.adapter=mAdapter
}
第三步:自定义一个Adapter,在最底层增加一个“正在加载中”的条目
该条目将成为新的最后一项数据,当该条目被显示的时候,触发上划加载更多回调。
在原来数据基础之上增加一个条目
override fun getItemCount(): Int {
return myGetItemCount() + 1
}
为该条目设置单独的viewtype来区分
override fun getItemViewType(position: Int): Int {
return if (position == myGetItemCount()) {
viewTypeLoading
} else {
myGetItemViewType(position)
}
}
当加载该条目的时候,进行特殊的“没有更多数据”条目显示
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (position == myGetItemCount()) {
(holder as LoadViewHolder).tvLoading.text = "没有更多数据"
tvLoadingTip = holder.tvLoading
holder.progressBarLoading.visibility = View.GONE
progressBarLoading = holder.progressBarLoading
} else {
myOnBindViewHolder(holder, position, myGetItemViewType(position))
}
}
该条目中的控件会被保存,用于修改加载状态为“正在加载中”还是“没有更多数据”
internal inner class LoadViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var tvLoading:TextView=itemView.findViewById(R.id.tv_loading)
var progressBarLoading: ProgressBar = itemView.findViewById(R.id.progressBar_loading)
}
fun showLoading() {
tvLoadingTip!!.text = "正在加载..."
progressBarLoading!!.visibility = View.VISIBLE
}
fun finishLoad() {
if (tvLoadingTip != null) tvLoadingTip!!.text = "没有更多数据"
if (progressBarLoading != null) progressBarLoading!!.visibility = View.GONE
}
另外实现一些必须实现的方法定为abstract要求子类去实现
protected abstract fun myOnCreateViewHolder(
viewGroup: ViewGroup,
viewType: Int
): RecyclerView.ViewHolder
protected abstract fun myOnBindViewHolder(
viewHolder: RecyclerView.ViewHolder,
position: Int,
viewType: Int
)
abstract fun myGetItemCount(): Int
protected abstract fun myGetItemViewType(position: Int): Int
到这adapter就修改完毕了。
现在这个下拉刷新上划加载更多的控件就准备完毕了,将在下一篇介绍具体的使用。