RecyclerView 的基本实现参考前篇:https://blog.csdn.net/whjk20/article/details/106950422
现实现 RecyclerView 的上拉刷新加载更多。
基本实现逻辑: 最后一条条目为加载更多, 有为两种状态,一是提示加载中, 二是加载失败提示重试。
目录
1. 增加 加载更多布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
app:cardUseCompatPadding="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/loading"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="90dp" >
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"/>
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:text="正在玩命的加载呢!!"
android:textSize="15sp"
android:gravity="center"
android:layout_height="match_parent"/>
</LinearLayout>
<TextView
android:id="@+id/reload"
android:text="加载失败,请重试"
android:layout_width="match_parent"
android:gravity="center"
android:layout_height="90dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
</RelativeLayout>
其中id:loading 为加载中, id:reload表示加载失败请重试
2. 定义数据类型
其实和之前普通的数据类型一样,只是方便区分而已
class UserDataLoadMore {
var userName: String
var userImageId: Int
constructor(userNameId: String, userImageId: Int, state: Int) {
this.userName = userNameId
this.userImageId = userImageId
}
}
3.修改Adapter
直接上完整代码
class ListViewLoadMoreNormalAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder> {
private var onRefreshListener: OnRefreshListener ?=null
private var datas: MutableList<UserDataLoadMore>
constructor(datas: MutableList<UserDataLoadMore>) {
this.datas = datas
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
var view: View
if (viewType == TYPE_LOAD_MORE) {
view = View.inflate(parent.context, R.layout.item_load_more, null)
return LoadMoreViewHolder(view)
} else {
view = View.inflate(parent.context, R.layout.item_list_view, null)
return NormalViewHolder(view)
}
}
override fun getItemCount(): Int {
return if (datas != null) {
datas.size
} else {
0
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is NormalViewHolder) {
holder.setData(datas[position])
} else if (holder is LoadMoreViewHolder) {
holder.update(holder.LOAD_STATE_LOADING)
}
}
override fun getItemViewType(position: Int): Int {
return if (position == datas.size - 1) {
TYPE_LOAD_MORE
} else {
TYPE_NORMAL
}
}
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)
}
}
inner class LoadMoreViewHolder : RecyclerView.ViewHolder {
val LOAD_STATE_NORMAL = 0
val LOAD_STATE_LOADING = 1
val LOAD_STATE_RELOAD = 2
private var loadingLayout: LinearLayout
private var reloadLayout: TextView
constructor(itemView: View) : super(itemView) {
this.loadingLayout = itemView.loading
this.reloadLayout = itemView.reload
this.reloadLayout.setOnClickListener{
update(LOAD_STATE_LOADING)
}
}
fun update(state: Int){
//初始化加载状态
loadingLayout.visibility = View.GONE
reloadLayout.visibility = View.GONE
when(state) {
LOAD_STATE_NORMAL -> {
loadingLayout.visibility = View.GONE
reloadLayout.visibility = View.GONE
}
LOAD_STATE_LOADING -> {
loadingLayout.visibility = View.VISIBLE
//还需要模拟数据加载
startLoadMore(this)
}
LOAD_STATE_RELOAD -> {
reloadLayout.visibility = View.VISIBLE
//此处需要响应点击事件
}
}
}
private fun startLoadMore(callBack: LoadMoreViewHolder) {
onRefreshListener?.onUpPullOnRefresh(callBack)
}
}
interface OnRefreshListener{
fun onUpPullOnRefresh(callBack: LoadMoreViewHolder)
}
fun setOnRefreshListener(listener: OnRefreshListener) {
this.onRefreshListener = listener
}
}
其中,
(1) getItemViewType 获取条目类型时,最后一条条目设置为加载更多状态
(2) LoadMoreViewHolder 内部有三种状态,表示加载的过程,Normal 表示已经完成加载,Loading表示加载中,reload表示加载失败了。
内部update() 函数表示更新状态,并调整加载界面显示(visible)
(3) 创建时需要为加载失败控件增加点击监听,使其再进入加载中状态。
(4)在绑定ViewHolder时,更新状态为Loading, 然后通过回调模拟数据更新,Activity注册监听(setOnRefreshListener)并且实现该回调
即适配器 提供刷新接口、注册刷新接口、刷新回调
private fun startLoadMore(callBack: LoadMoreViewHolder) {
onRefreshListener?.onUpPullOnRefresh(callBack)
}
4. Activity 注册刷新监听
private fun initListener() {
// 匿名内部类
loadMoreNormalAdapter.setOnRefreshListener(object :
ListViewLoadMoreNormalAdapter.OnRefreshListener {
// 还需要持有adapter viewholder 的引用,才能模拟加载失败的状态
override fun onUpPullOnRefresh(callBack: ListViewLoadMoreNormalAdapter.LoadMoreViewHolder) {
randomAddDatas(callBack)
}
})
}
(1) 其中randomAddDatas 表示模拟随机添加数据,然后更新UI,
(2)当新增随机数目为0时,则设置ViewHolder 状态为reload ,表示加载失败,则会更新最后一条条目状态为重新加载。
Activity 完整代码
class ListViewLoadMoreNormalActivity : AppCompatActivity() {
private lateinit var loadMoreNormalAdapter: ListViewLoadMoreNormalAdapter
private var datas = mutableListOf<UserDataLoadMore>()
var random = Random(PIC_IDS.size)
private lateinit var context: Context
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_load_more)
context = this
initDatas()
loadMoreNormalAdapter =
ListViewLoadMoreNormalAdapter(datas)
initListener()
showListView()
}
private fun initListener() {
// 尝试使用懒惰表达代替 匿名内部类
loadMoreNormalAdapter.setOnRefreshListener(object :
ListViewLoadMoreNormalAdapter.OnRefreshListener {
// 还需要持有adapter viewholder 的引用,才能模拟加载失败的状态
override fun onUpPullOnRefresh(callBack: ListViewLoadMoreNormalAdapter.LoadMoreViewHolder) {
randomAddDatas(callBack)
}
})
}
private fun showListView() {
//2. 创建并设置布局管理器
var layoutManager = LinearLayoutManager(context)
layoutManager.orientation = LinearLayoutManager.VERTICAL
recycler_view.layoutManager = layoutManager
// 3. 创建并设置adapter
recycler_view.adapter = loadMoreNormalAdapter
}
private fun initDatas() {
datas.clear()
addDatas(PIC_IDS.size)
}
private fun addDatas(addSize: Int) {
if (addSize < 0) {
return
}
if (addSize == 0) {
}
for (index in 0 until addSize) {
var userData = UserDataLoadMore(
context.resources.getString(CommonConstants.USER_NAMES[index % PIC_IDS.size]),
PIC_IDS[index % PIC_IDS.size]
)
datas.add(userData)
}
}
fun randomAddDatas(callBack: ListViewLoadMoreNormalAdapter.LoadMoreViewHolder) {
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()
if (newSize == 0) {
callBack.update(callBack.LOAD_STATE_RELOAD)
} else {
addDatas(newSize)
loadMoreNormalAdapter.notifyDataSetChanged()
}
}, 3000)
}
}
此外,上拉刷新还可以对RecyclerView 添加滑动监听 (addOnScrollListener), 然后通过回调进行模拟数据更新,
相对会复杂些。后续再总结