Как отличить начальную загрузку от изменения содержимого в Android Paging 3?

У меня есть RecyclerView, который использует PagingDataAdapter для отображения своих элементов. При начальной загрузке всей страницы я хочу показать заполнитель загрузки 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 имеет только одно состояние загрузки обновления как для начальной загрузки, так и для изменений содержимого. Есть ли другой способ их различить?

1 ответ

Решение

Ты используешь ? Если это так, вы можете просто наблюдать изменения в 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, но как бы нужно более точно определить, что вы подразумеваете под начальной загрузкой. Например, считается ли изменение конфигурации или смерть процесса начальной загрузкой, или вас просто волнует случай без кеширования (в памяти и в базе данных)?

Другие вопросы по тегам