RecyclerView 的上拉刷新加载更多的基本实现 参考上篇: https://blog.csdn.net/whjk20/article/details/106975642
上拉刷新还可以对RecyclerView 添加滑动监听 (addOnScrollListener), 然后通过回调进行模拟数据更新
这里主要把加载更多插到RecyclerView 最后, 并非RecyclerView的数据中。
目录
1.布局文件 (分开加载中和加载失败)
此处省略,跟之前的差异不大,只是分成两个文件
2. 数据类型(增加一个数据状态标志位)
package com.example.androidrecyclerviewtest.data
class UserDataLoadMore {
var userName: String
var userImageId: Int
var state: Int
// state 在adpater 的getItemViewType中用到
constructor(userNameId: String, userImageId: Int, state: Int) {
this.userName = userNameId
this.userImageId = userImageId
this.state = state
}
}
其中有三种状态:
const val TYPE_NORMAL = 0
const val TYPE_LOADING = 1
const val TYPE_RELOAD = 2
3. 修改Adapter
class ListViewLoadMoreScrollAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder> {
//方法2: 通过设置接口回调,则不需要持有具体类的引用
private var loadMoreScrollActivity: ListViewLoadMoreScrollActivity
private var datas: MutableList<UserDataLoadMore>
constructor(loadMoreScrollActivity: ListViewLoadMoreScrollActivity, datas: MutableList<UserDataLoadMore>) {
this.loadMoreScrollActivity = loadMoreScrollActivity
this.datas = datas
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
var view: View
return if (viewType == TYPE_LOADING) {
view = View.inflate(parent.context, R.layout.item_loading, null)
LoadingViewHolder(view)
} else {
if (viewType == TYPE_RELOAD) {
view = View.inflate(parent.context, R.layout.item_reload, null)
ReloadViewHolder(view)
} else {
view = View.inflate(parent.context, R.layout.item_list_view, null)
NormalViewHolder(view)
}
}
}
//加一个,最后一个为加载中(并没有加到datas 里)
override fun getItemCount(): Int {
return if (datas != null) {
//最后一个为reload, 则表示不需要加上 loading
if (datas.size >= 1 && datas[datas.size - 1].state == TYPE_RELOAD) {
datas.size
} else {
datas.size + 1
}
} else {
0
}
}
override fun getItemViewType(position: Int): Int {
return if (position >= datas.size) {
TYPE_LOADING
} else {
//TYPE_NORMAL
datas[position].state
}
//return datas[position].state
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is NormalViewHolder) {
holder.setData(datas[position])
}
}
class NormalViewHolder : RecyclerView.ViewHolder {
private var image: ImageView
private var nameText: TextView
//private var state: Int = TYPE_NORMAL
constructor(itemView: View) : super(itemView) {
this.nameText = itemView.user_name
this.image = itemView.user_image
}
fun setData(userData: UserDataLoadMore) {
nameText.text = userData.userName
image.setImageResource(userData.userImageId)
}
}
class LoadingViewHolder : RecyclerView.ViewHolder {
//private var state: Int = TYPE_LOADING
constructor(itemView: View) : super(itemView) {
}
}
inner class ReloadViewHolder : RecyclerView.ViewHolder {
//private var state: Int = TYPE_LOADING
constructor(itemView: View) : super(itemView) {
itemView.retry.setOnClickListener {
//先一处重新加载的(加载失败), 这里需要 size-1!!!
datas.removeAt(datas.size - 1)
loadMoreScrollActivity.randomAddDatas()
}
}
}
}
3.修改Activity
class ListViewLoadMoreScrollActivity : AppCompatActivity() {
private var datas = mutableListOf<UserDataLoadMore>()
private lateinit var context: Context
var random = Random(PIC_IDS.size)
var isLoading = false
lateinit var loadMoreScrollAdapter: ListViewLoadMoreScrollAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_load_more)
context = this
initDatas()
loadMoreScrollAdapter = ListViewLoadMoreScrollAdapter(this, datas)
showListView()
}
private fun initDatas() {
datas.clear()
addDatas(PIC_IDS.size)
}
private fun addDatas(addSize: Int) {
if (addSize < 0) {
return
}
// 模拟加载失败的情况,加载到最后一个(loading的并没有加到datas里),reload的也不应该加载到datas里的
if (addSize == 0) {
datas.add(UserDataLoadMore("失败了", PIC_IDS[0], TYPE_RELOAD))
return
}
for (index in 0 until addSize) {
var userData = UserDataLoadMore(
context.resources.getString(USER_NAMES[index % PIC_IDS.size]),
PIC_IDS[index % PIC_IDS.size],
TYPE_NORMAL
)
datas.add(userData)
}
}
private fun showListView() {
//2. 创建并设置布局管理器
var layoutManager = LinearLayoutManager(context)
layoutManager.orientation = LinearLayoutManager.VERTICAL
recycler_view.layoutManager = layoutManager
// 3. 创建并设置adapter
recycler_view.adapter = loadMoreScrollAdapter
// 5. 上拉刷新
recycler_view.addOnScrollListener(object : RecyclerView.OnScrollListener() {
// 如何防止一次滑动,多次调用导致的多次更新数据??? --> 判断 SCROLL_STATE_IDLE 才进行刷新
// 还是会有重复刷新的情况 --> 延迟添加数据导致的,因此判断如果已经正在加载,则不再进行
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
var adapter = recyclerView.adapter
var layoutManager = recyclerView.layoutManager
if (layoutManager is LinearLayoutManager) {
adapter?.let {
if (layoutManager.findLastCompletelyVisibleItemPosition() == adapter.itemCount - 1
&& newState == RecyclerView.SCROLL_STATE_IDLE
&& !isLoading
) {
var handler = Handler()
isLoading = true
/* handler.postDelayed({
//随机添加数据 - random 每次都是从1开始,所以不能在这里定义random (也会有0的情况)
// 0的时候,判断为失败???
var newSize = random.nextInt(PIC_IDS.size)
Toast.makeText(context, "newSize = $newSize", Toast.LENGTH_SHORT)
.show()
addDatas(newSize)
isLoading = false
adapter.notifyDataSetChanged()
}, 3000)*/
randomAddDatas()
}
}
}
super.onScrollStateChanged(recyclerView, newState)
}
})
}
fun randomAddDatas() {
var handler = Handler()
handler.postDelayed({
//随机添加数据 - random 每次都是从1开始,所以不能在这里定义random (也会有0的情况)
// 0的时候,判断为失败???
var newSize = random.nextInt(PIC_IDS.size)
Toast.makeText(context, "newSize = $newSize", Toast.LENGTH_SHORT)
.show()
addDatas(newSize)
isLoading = false
loadMoreScrollAdapter.notifyDataSetChanged()
}, 3000)
}
}
逻辑也很好理解,当滑动到最后一个条目时,进行模拟数据更新,并触发更新UI.
只是onScrollStateChanged 里面的状态要注意些,以防重复刷新
其实还是有很多问题的,只是为了学习上拉刷新的基本逻辑,后续再优化