본문 바로가기
Kotlin

[Paging] UI에 로드 상태 표시

by 명훈스토리 2023. 2. 20.
SMALL

Paging 라이브러리에서 UI에 표시할 항목을 더 많이 가져올 때는 사용자에게 더 많은 데이터를 가져오고 있다고 표시하는 것이 좋다. Paging 라이브러리는 CombinedLoadState 유형으로 로드 상태에 간단하게 액세스하는 방법을 제공한다.

 

CombinedLoadState 인스턴스는 데이터를 로드하는 Paging 라이브러리에 있는 모든 구성요소의 로드 상태를 설명한다. 여기서는 ArticlePagingSource의 LoadState에만 관심이 있으므로 주로 CombinedLoadState.source 필드의 LoadStates 유형을 사용한다.

 

PagingDataAdapter.loadStateFlow를 통해 PagingDataAdapter를 통한 CombinedLoadStates에 액세스할 수 있다.

 

※ 로드 상태를 설명하는데 도움이 되는 세 가지 고유한 클래스는 CombinedLoadStates, LoadStates, LoadState이다. CombinedLoadState에는 LoadStates 인스턴스가 있고 LoadStates는 LoadState 인스턴스를 제공한다.

 

CombinedLoadStates.source는 LoadStates 유형으로 세 가지 유형의 LoadState 필드가 있다.

  • LoadStates.append: 사용자의 현재 위치 후에 가져오는 항목의 LoadState용
  • LoadStates.prepend: 사용자의 현재 위치 전에 가져오는 항목의 LoadState용
  • LoadStates.refresh: 초기 로드의 LoadState용

각 LoadState 자체는 다음 중 하나일 수 있다

  • LoadState.Loading: 항목을 로드하고 있다
  • LoadState.NotLoading: 항목을 로드하고 있지 않다
  • LoadState.Error: 로드 오류가 발생

여기서는 LoadState가 LoadState.Loading인지만 중요하다. ArticlePagingSource에 오류 사례가 포함되어 있지 않기 때문이다.

 

가장 먼저 할 일은 UI 상단과 하단에 진행률 표시줄을 추가하여 양방향으로 가져오기의 로드 상태를 나타내는 것이다.

 

activity_articles.xml에서 다음과 같이 LinearProgressIndicator 막대 두 개를 추가한다.

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.ArticleActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/list"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scrollbars="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.progressindicator.LinearProgressIndicator
        android:id="@+id/prepend_progress"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:indeterminate="true"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.progressindicator.LinearProgressIndicator
        android:id="@+id/append_progress"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:indeterminate="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

이제 PagingDataAdapter에서 LoadStatesFlow를 수집하여 CombinedLoadState에 반응한다. ArticleActivity.kt에서 상태를 수집한다.

 

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                articleAdapter.loadStateFlow.collect {
                    binding.prependProgress.isVisible = it.source.prepend is Loading
                    binding.appendProgress.isVisible = it.source.append is Loading
                }
            }
        }
        lifecycleScope.launch {
        ...
    }

마지막으로 ArticlePagingSource에 delay를 추가하여 로드한다.

 

private const val LOAD_DELAY_MILLIS = 3_000L

class ArticlePagingSource : PagingSource<Int, Article>() {

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Article> {
        val start = params.key ?: STARTING_KEY
        val range = startKey.until(startKey + params.loadSize)

        if (start != STARTING_KEY) delay(LOAD_DELAY_MILLIS)
        return ...

}

 

지금까지 다룬 내용을 간략하게 요약해보면 다음과 같다.

  • 페이지로 나누기에 관해 간략히 살펴보고 필요한 이유를 알아봄.
  • Pager를 만들고 PagingSource를 정의하고 PagingData를 내보내 앱에 페이지로 나누기를 추가
  • cachedIn 연산자를 사용하여 ViewModel에 PagingData를 캐시
  • PagingDataAdapter를 사용하여 UI에서 PagingData를 사용
  • PagingDataAdapter.loadStateFlow를 사용하여 CombinedLoadStates에 반
LIST

댓글