feat(meal): 添加餐品列表下拉刷新和上拉加载功能

- 集成 SmartRefreshLayout 实现下拉刷新和上拉加载更多
- 修改 MealListActivity 添加分页加载逻辑,每页30条数据
- 实现空视图显示功能,当无数据时展示暂无数据提示
- 优化 RecyclerView 点击事件处理,使用 startActivity 扩展函数
- 添加加载状态管理,区分首次加载和后续加载情况
- 实现加载失败时的状态回退和错误处理机制
- 统一网络请求回调处理,优化 Loading 状态显示逻辑
This commit is contained in:
2026-06-05 10:13:05 +08:00
parent 2f18376236
commit c8383af120
3 changed files with 96 additions and 25 deletions
@@ -114,7 +114,8 @@ class NetViewModelV2(
) {
viewModelScope.launch {
onLoading()
onResult(repository.getFoodOptions(keyword, pageNum, pageSize))
val state = repository.getFoodOptions(keyword, pageNum, pageSize)
onResult(state)
}
}
@@ -2,14 +2,17 @@ package com.shuwei.dish.match.ui
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import androidx.recyclerview.widget.LinearLayoutManager
import com.shuwei.dish.match.adapter.MealListAdapter
import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.databinding.ActivityMealListBinding
import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding
import com.shuwei.dish.match.model.NutFoodOptionVO
import com.shuwei.dish.match.net.NetViewModelV2
import com.shuwei.dish.match.net.UiState
import com.shuwei.dish.match.utils.ext.gone
import com.shuwei.dish.match.utils.ext.startActivity
import com.shuwei.dish.match.utils.ext.toast
import com.shuwei.dish.match.utils.ext.visible
@@ -20,13 +23,24 @@ class MealListActivity : BaseActivity() {
companion object {
const val MEAL_ITEM = "mealItem"
const val PAGE_SIZE = 30
}
private val binding by lazy { ActivityMealListBinding.inflate(layoutInflater) }
private val viewModelV2 by lazy { NetViewModelV2() }
private val mealList = mutableListOf<NutFoodOptionVO>()
private val mealAdapter by lazy { MealListAdapter(mealList) }
private val mealAdapter by lazy { MealListAdapter(mealList).apply {
isStateViewEnable = true
setOnItemClickListener { _, _, position ->
val item = mealList.getOrNull(position) ?: return@setOnItemClickListener
startActivity<PackActivity>{
putExtra(PackActivity.MEAL_ITEM, item)
}
}
} }
private var emptyViewBinding: LayoutEmptyViewBinding? = null
private var currentPage = 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -54,12 +68,15 @@ class MealListActivity : BaseActivity() {
private fun initRecyclerView() {
binding.rvMealList.layoutManager = LinearLayoutManager(this)
binding.rvMealList.adapter = mealAdapter
mealAdapter.setOnItemClickListener { _, _, position ->
val item = mealList.getOrNull(position) ?: return@setOnItemClickListener
val intent = Intent(this, PackActivity::class.java).apply {
putExtra(PackActivity.MEAL_ITEM, item)
}
startActivity(intent)
binding.refreshLayout.setEnableRefresh(true)
binding.refreshLayout.setEnableLoadMore(true)
binding.refreshLayout.setOnRefreshListener {
currentPage = 1
loadMealList()
}
binding.refreshLayout.setOnLoadMoreListener {
currentPage++
loadMealList()
}
}
@@ -67,32 +84,70 @@ class MealListActivity : BaseActivity() {
* 加载餐品列表
*/
private fun loadMealList() {
showLoading("加载中……")
viewModelV2.getFoodOptionsWithCallback(
keyword = null,
pageNum = 1,
pageSize = 100,
onLoading = {
showLoading("加载中……")
},
pageNum = currentPage,
pageSize = PAGE_SIZE,
onLoading = {},
onResult = { state ->
dismissLoading()
when (state) {
is UiState.Success -> {
val records = state.data?.records
if (records.isNullOrEmpty()) {
toast("暂无数据")
if (currentPage == 1 && records.isNullOrEmpty()) {
binding.refreshLayout.finishRefresh()
loadEmptyView()
return@getFoodOptionsWithCallback
}
mealList.clear()
mealList.addAll(records)
if (currentPage == 1) {
mealList.clear()
}
mealList.addAll(records!!)
mealAdapter.notifyDataSetChanged()
val isLoadMore = records.size >= PAGE_SIZE
binding.refreshLayout.setEnableLoadMore(isLoadMore)
if (isLoadMore) {
binding.refreshLayout.finishLoadMore()
} else {
binding.refreshLayout.finishLoadMoreWithNoMoreData()
}
}
is UiState.Error -> {
toast(state.msg)
if (currentPage == 1) {
binding.refreshLayout.finishRefresh()
loadEmptyView()
} else {
currentPage--
binding.refreshLayout.finishLoadMore()
}
}
else -> {}
}
}
)
}
/**
* 加载空视图
*/
private fun loadEmptyView() {
mealList.clear()
mealAdapter.notifyDataSetChanged()
if (emptyViewBinding == null) {
emptyViewBinding = LayoutEmptyViewBinding.inflate(
LayoutInflater.from(this), binding.rvMealList, false
)
}
emptyViewBinding?.run {
tvContent.text = "暂无数据"
tvSubContent.text = "暂无餐品数据"
mealAdapter.stateView = root
}
binding.refreshLayout.run {
setEnableRefresh(true)
setEnableLoadMore(false)
}
}
}
+23 -8
View File
@@ -26,15 +26,30 @@
android:textColor="@color/black666"
android:textSize="28sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvMealList"
<com.scwang.smart.refresh.layout.SmartRefreshLayout
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="20dp"
android:overScrollMode="never"
android:scrollbars="vertical"
tools:itemCount="3"
tools:listitem="@layout/list_item_food2" />
android:layout_height="match_parent">
<com.scwang.smart.refresh.header.ClassicsHeader
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvMealList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="20dp"
android:overScrollMode="never"
android:scrollbars="vertical"
tools:itemCount="3"
tools:listitem="@layout/list_item_food2" />
<com.scwang.smart.refresh.footer.ClassicsFooter
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
</LinearLayout>