마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

다중 뷰타입의 페이징3 사용 질문

0 추천
PagingDataAdapter(Paging3 라이브러리)를 상속한 어댑터가 다음과 같은 아이템을 가지고 있다 할 때,

아이템 1 - 뷰홀더 1

아이템 2 - 뷰홀더 2

아이템 3 - 뷰홀더 2

아이템 4 - 뷰홀더 2

...

아이템 n - 뷰홀더 2

 

아이템 2부터 페이징을 적용하려면 어떻게 처리해야 할까요?

PagingSource(뷰홀더 2의 데이터를 불러옴)에서 어댑터의 아이템 1을 포함하여 계산해서 뭔가 오작동이 발생할 거 같은데, 아닌가요?

아직 구현해보지는 않았습니다.
바라던바밤바 (160 포인트) 님이 2022년 4월 26일 질문

1개의 답변

0 추천

PagingData를 이용해 볼 수 있지 않을까하는 생각이 드네요.
https://developer.android.com/reference/kotlin/androidx/paging/PagingData#(androidx.paging.PagingData).insertHeaderItem(androidx.paging.TerminalSeparatorType,kotlin.Any)

 

fun <T : Any!, R : Any!> PagingData<T!>!.flatMap(transform: (suspend (T) -> Iterable<R>)?): PagingData<R>

fun <T : Any!> PagingData<T!>!.insertHeaderItem(
    terminalSeparatorType: TerminalSeparatorType! = FULLY_COMPLETE,
    item: T!
): PagingData<T>

위의 PagingData 함수 중 하나를 이용해서 처리해 볼 수 있을 것 같은데요.

PagingData 함수 중에 InsertHeaderItem이 있는데, 데이터를 불러오고 나서 collect할 때 pagingData에 원하시는 아이템을 집어넣으시면 어떨가 생각하는데요. flatMap과 같이 사용하면 아래와 같은 형태가 될 것 같네요. 테스트는 직접 해보시기 바랍니다.

pagingFlow
.flatMap { pagingData -> {
      val firstItm = getFirstItem()
      pagingData.insertHeaderItem { before: String?, after: String? ->
        if (bofore == null && after != null) {
            firstItm
        } else {
           null
       }
    }
}

 

 

spark (224,220 포인트) 님이 2022년 4월 26일 답변
spark님이 2022년 4월 26일 수정
ConcatAdapter가 가능한지도 살펴보세요. 이론적으로는 될 것 같은데, 이게 된다면 더 쉬운옵션이긴 합니다.
https://developer.android.com/reference/androidx/recyclerview/widget/ConcatAdapter
 FirstItemAdapter adapter1 = ...;
 PagingAdapter adapter2 = ...;
 ConcatAdapter concatenated = new ConcatAdapter(adapter1, adapter2);
 recyclerView.setAdapter(concatenated);
답변 감사합니다. 시도해보겠습니다!
말씀해주신대로, InsertHeaderItem를 사용해서 해결했습니다!

먼저 PagingDataAdapter의 Item으로 담을 클래스를 하나 만들어주고,

sealed class MovieItem(val type: MovieType) {
    data class Data(val value: Movies.Movie): MovieItem(MovieType.DATA)
    object Header: MovieItem(MovieType.HEADER)
}

getItemViewType을 override하여 아이템의 타입에 따라 뷰타입을 설정해줬습니다.

companion object {

        const val VIEW_TYPE_SEARCH = 0
        const val VIEW_TYPE_MOVIE = 1
}

override fun getItemViewType(position: Int): Int {
        getItem(position)?.let {
            return if(it.type == MovieType.HEADER) VIEW_TYPE_SEARCH else VIEW_TYPE_MOVIE
        } ?: kotlin.run {
            return VIEW_TYPE_MOVIE
        }
    }

PagingData에 HeaderItem을 삽입하여 해결했습니다.

viewModel.movies.observe(viewLifecycleOwner) { pagingData ->
            adapter.submitData(lifecycle, pagingData.map { MovieItem.Data(it) as MovieItem }
                .insertHeaderItem(item = MovieItem.Header))

ConcatAdapter도 나중에 적용해보겠습니다. 감사합니다!
잘 됐네요. 축하드려요.
Int 대신에 enum을 사용하거나 int상수가 layout Id를 가키리게 하셔도 좋습니다.
enum class MovieType(@LayoutRes val layoutId) {
    HEADER(R.layout.item_search),
    DATA(R.layout.item_movie)
}

sealed class MovieItem(val type: MovieType) {
    data class Data(val value: Movies.Movie): MovieItem(MovieType.DATA)
    object Header: MovieItem(MovieType.HEADER)

    val layoutId:  Int= type.layoutId
}

override fun getItemViewType(position: Int): Int {
        getItem(position)?.layoutId
    }
...