如何区分 Android Paging 3 中的初始加载和内容更改?
How to distinguish between initial load and content change in Android Paging 3?
我有一个使用 PagingDataAdapter 来显示其项目的 RecyclerView。在整个页面的初始加载时,我想显示一个 Shimmer 加载占位符。但是,当我尝试这样做时,加载占位符也会显示为单个项目的小内容更改。这会使整个屏幕闪烁,因为它在加载期间隐藏了 RecyclerView,并在加载内容更改后重新显示它。我不希望加载占位符显示单个内容更改。
我正在检查适配器的加载状态侦听器中的加载状态:
addLoadStateListener {
val taskListState = when (it.refresh) { // triggered for both initial load and content change
is LoadState.Loading -> ScheduleViewModel.TaskListState.LOADING
// I want something like if (loadStateIsForInitialLoad()) ScheduleViewModel.TaskListState.LOADING else ScheduleViewModel.TaskListState.PRESENT
is LoadState.Error -> ScheduleViewModel.TaskListState.ERROR
is LoadState.NotLoading ->
if (it.append.endOfPaginationReached && itemCount < 1) {
ScheduleViewModel.TaskListState.EMPTY
} else {
ScheduleViewModel.TaskListState.PRESENT
}
}
scheduleViewModel.setTaskListState(taskListState)
}
内容更改通过数据库:
class ScheduleViewModel @Inject constructor(private val taskRepository: TaskRepository) :
ViewModel() {
fun updateDoneState(task: TaskItem) {
viewModelScope.launch(Dispatchers.IO) {
if (task.isDone) {
taskRepository.markUndone(task.id)
} else {
taskRepository.markDone(task.id)
}
}
}
}
布局:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="viewModel" type="ogbe.eva.prompt.ui.schedule.ScheduleViewModel"/>
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/shimmerFrameLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:isVisible="@{viewModel.isLoading}">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/item_task_placeholder"/>
<include layout="@layout/item_task_placeholder"/>
<include layout="@layout/item_task_placeholder"/>
<include layout="@layout/item_task_placeholder"/>
<include layout="@layout/item_task_placeholder"/>
</LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/task_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/spacing_sm"
app:isVisible="@{viewModel.isPresent}"/>
</FrameLayout>
</layout>
看起来 CombinedLoadStates 对于初始加载和内容更改只有一个刷新加载状态。有没有其他方法可以区分它们?
您在使用 RemoteMediator
吗?如果是这样,您可以观察 CombinedLoadStates.mediator.refresh
的变化。 CombinedLoadStates.refresh
只是一个帮助程序,它结合了“常见”用例的调解器 + 源状态。
如果您仅使用 PagingSource
并从 RemoteMediator
单独更新数据库/失效,您还可以检查 adapter.itemCount
以确定“初始负载”。
adapter.addLoadStateListener { loadStates ->
when (loadStates.source.refresh) {
is NotLoading -> {
// Always redundantly disable loading spinner here, which assumes doing so
// is stateless.
if (loadStates.source.refresh is NotLoading) {
scheduleViewModel.setTaskListState(
if (it.append.endOfPaginationReached && itemCount < 1) {
ScheduleViewModel.TaskListState.EMPTY
} else {
ScheduleViewModel.TaskListState.PRESENT
}
)
}
}
is LoadState.Loading -> {
if (adapter.itemCount == 0) {
ScheduleViewModel.TaskListState.LOADING
} else {
ScheduleViewModel.TaskListState.PRESENT
}
}
is LoadState.Error -> ScheduleViewModel.TaskListState.ERROR
}
}
否则,您也可以自己跟踪并与 loadStateFlow
混合使用,但需要更准确地定义初始加载的含义。例如,配置更改或进程死亡算作初始加载,还是您只关心非缓存(在内存和数据库中)的情况?
我有一个使用 PagingDataAdapter 来显示其项目的 RecyclerView。在整个页面的初始加载时,我想显示一个 Shimmer 加载占位符。但是,当我尝试这样做时,加载占位符也会显示为单个项目的小内容更改。这会使整个屏幕闪烁,因为它在加载期间隐藏了 RecyclerView,并在加载内容更改后重新显示它。我不希望加载占位符显示单个内容更改。
我正在检查适配器的加载状态侦听器中的加载状态:
addLoadStateListener {
val taskListState = when (it.refresh) { // triggered for both initial load and content change
is LoadState.Loading -> ScheduleViewModel.TaskListState.LOADING
// I want something like if (loadStateIsForInitialLoad()) ScheduleViewModel.TaskListState.LOADING else ScheduleViewModel.TaskListState.PRESENT
is LoadState.Error -> ScheduleViewModel.TaskListState.ERROR
is LoadState.NotLoading ->
if (it.append.endOfPaginationReached && itemCount < 1) {
ScheduleViewModel.TaskListState.EMPTY
} else {
ScheduleViewModel.TaskListState.PRESENT
}
}
scheduleViewModel.setTaskListState(taskListState)
}
内容更改通过数据库:
class ScheduleViewModel @Inject constructor(private val taskRepository: TaskRepository) :
ViewModel() {
fun updateDoneState(task: TaskItem) {
viewModelScope.launch(Dispatchers.IO) {
if (task.isDone) {
taskRepository.markUndone(task.id)
} else {
taskRepository.markDone(task.id)
}
}
}
}
布局:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="viewModel" type="ogbe.eva.prompt.ui.schedule.ScheduleViewModel"/>
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/shimmerFrameLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:isVisible="@{viewModel.isLoading}">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/item_task_placeholder"/>
<include layout="@layout/item_task_placeholder"/>
<include layout="@layout/item_task_placeholder"/>
<include layout="@layout/item_task_placeholder"/>
<include layout="@layout/item_task_placeholder"/>
</LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/task_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/spacing_sm"
app:isVisible="@{viewModel.isPresent}"/>
</FrameLayout>
</layout>
看起来 CombinedLoadStates 对于初始加载和内容更改只有一个刷新加载状态。有没有其他方法可以区分它们?
您在使用 RemoteMediator
吗?如果是这样,您可以观察 CombinedLoadStates.mediator.refresh
的变化。 CombinedLoadStates.refresh
只是一个帮助程序,它结合了“常见”用例的调解器 + 源状态。
如果您仅使用 PagingSource
并从 RemoteMediator
单独更新数据库/失效,您还可以检查 adapter.itemCount
以确定“初始负载”。
adapter.addLoadStateListener { loadStates ->
when (loadStates.source.refresh) {
is NotLoading -> {
// Always redundantly disable loading spinner here, which assumes doing so
// is stateless.
if (loadStates.source.refresh is NotLoading) {
scheduleViewModel.setTaskListState(
if (it.append.endOfPaginationReached && itemCount < 1) {
ScheduleViewModel.TaskListState.EMPTY
} else {
ScheduleViewModel.TaskListState.PRESENT
}
)
}
}
is LoadState.Loading -> {
if (adapter.itemCount == 0) {
ScheduleViewModel.TaskListState.LOADING
} else {
ScheduleViewModel.TaskListState.PRESENT
}
}
is LoadState.Error -> ScheduleViewModel.TaskListState.ERROR
}
}
否则,您也可以自己跟踪并与 loadStateFlow
混合使用,但需要更准确地定义初始加载的含义。例如,配置更改或进程死亡算作初始加载,还是您只关心非缓存(在内存和数据库中)的情况?