Android Kotlin 기초 07.5: RecyclerView의 헤더

이 Codelab은 Android Kotlin 기초 과정의 일부입니다. Codelab을 순서대로 진행하면 이 과정의 학습 효과를 극대화할 수 있습니다. 모든 과정 Codelab은 Android Kotlin 기본사항 Codelab 방문 페이지에 나열되어 있습니다.

소개

이 Codelab에서는 RecyclerView에 표시된 목록의 너비에 걸쳐 있는 헤더를 추가하는 방법을 알아봅니다. 이전 Codelab의 수면 추적기 앱을 기반으로 빌드합니다.

기본 요건

  • 활동, 프래그먼트, 뷰를 사용하여 기본 사용자 인터페이스를 빌드하는 방법
  • 프래그먼트 간 이동 방법과 safeArgs을 사용하여 프래그먼트 간에 데이터를 전달하는 방법
  • 모델, 모델 팩토리, 변환, LiveData 및 관찰자를 확인합니다.
  • Room 데이터베이스를 만들고, DAO를 만들고, 항목을 정의하는 방법
  • 데이터베이스 상호작용 및 기타 장기 실행 작업에 코루틴을 사용하는 방법
  • Adapter, ViewHolder, 항목 레이아웃으로 기본 RecyclerView를 구현하는 방법
  • RecyclerView의 데이터 바인딩을 구현하는 방법
  • 바인딩 어댑터를 만들어 데이터를 변환하는 방법
  • GridLayoutManager 사용 방법
  • RecyclerView.의 항목 클릭을 캡처하고 처리하는 방법

학습할 내용

  • RecyclerView와 함께 두 개 이상의 ViewHolder를 사용하여 레이아웃이 다른 항목을 추가하는 방법 특히 두 번째 ViewHolder를 사용하여 RecyclerView에 표시된 항목 위에 헤더를 추가하는 방법을 설명합니다.

실습할 내용

  • 이 시리즈의 이전 Codelab에서 TrackMySleepQuality 앱을 기반으로 빌드합니다.
  • RecyclerView에 표시된 수면 밤 위에 화면 너비에 걸쳐 있는 헤더를 추가합니다.

시작하는 수면 추적기 앱에는 아래 그림과 같이 프래그먼트로 표시되는 화면이 세 개 있습니다.

왼쪽에 표시된 첫 번째 화면에는 추적을 시작하고 중지하는 버튼이 있습니다. 화면에는 사용자의 수면 데이터가 일부 표시됩니다. 지우기 버튼은 앱이 사용자를 위해 수집한 모든 데이터를 완전히 삭제합니다. 가운데에 표시된 두 번째 화면은 수면의 질 평가를 선택하는 화면입니다. 세 번째 화면은 사용자가 그리드에서 항목을 탭할 때 열리는 세부정보 뷰입니다.

이 앱은 UI 컨트롤러, 뷰 모델, LiveData, Room 데이터베이스를 사용하여 수면 데이터를 유지하는 간소화된 아키텍처를 사용합니다.

이 Codelab에서는 표시된 항목 그리드에 헤더를 추가합니다. 최종 기본 화면은 다음과 같이 표시됩니다.

이 Codelab에서는 다양한 레이아웃을 사용하는 항목을 RecyclerView에 포함하는 일반적인 원칙을 알아봅니다. 일반적인 예로는 목록이나 그리드에 헤더가 있는 경우를 들 수 있습니다. 목록에는 항목 콘텐츠를 설명하는 단일 헤더가 있을 수 있습니다. 목록에는 단일 목록에서 항목을 그룹화하고 구분하는 여러 헤더가 있을 수도 있습니다.

RecyclerView는 데이터나 각 항목의 레이아웃 유형에 대해 알지 못합니다. LayoutManager는 화면에 항목을 정렬하지만 어댑터는 표시할 데이터를 조정하고 뷰 홀더를 RecyclerView에 전달합니다. 따라서 어댑터에 헤더를 만드는 코드를 추가합니다.

헤더를 추가하는 두 가지 방법

RecyclerView에서 목록의 각 항목은 0부터 시작하는 색인 번호에 해당합니다. 예를 들면 다음과 같습니다.

[실제 데이터] -> [어댑터 뷰]

[0: SleepNight] -> [0: SleepNight]

[1: SleepNight] -> [1: SleepNight]

[2: SleepNight] -> [2: SleepNight]

목록에 헤더를 추가하는 한 가지 방법은 헤더를 표시해야 하는 색인을 확인하여 다른 ViewHolder를 사용하도록 어댑터를 수정하는 것입니다. Adapter는 헤더를 추적할 책임이 있습니다. 예를 들어 표 상단에 헤더를 표시하려면 0부터 시작하는 항목을 배치하는 동안 헤더에 다른 ViewHolder를 반환해야 합니다. 그러면 아래와 같이 다른 모든 항목이 헤더 오프셋으로 매핑됩니다.

[실제 데이터] -> [어댑터 뷰]

[0: Header]

[0: SleepNight] -> [1: SleepNight]

[1: SleepNight] -> [2: SleepNight]

[2: SleepNight] -> [3: SleepNight.

헤더를 추가하는 또 다른 방법은 데이터 그리드의 지원 데이터 세트를 수정하는 것입니다. 표시해야 하는 모든 데이터가 목록에 저장되므로 목록을 수정하여 헤더를 나타내는 항목을 포함할 수 있습니다. 이 방법은 이해하기가 약간 더 쉽지만, 여러 항목 유형을 단일 목록으로 결합할 수 있도록 객체를 설계하는 방법을 고려해야 합니다. 이런 방식으로 구현하면 어댑터가 전달된 항목을 표시합니다. 따라서 위치 0의 항목은 헤더이고 위치 1의 항목은 화면에 직접 매핑되는 SleepNight입니다.

[실제 데이터] -> [어댑터 뷰]

[0: Header] -> [0: Header]

[1: SleepNight] -> [1: SleepNight]

[2: SleepNight] -> [2: SleepNight]

[3: SleepNight] -> [3: SleepNight]

각 방법론에는 장단점이 있습니다. 데이터 세트를 변경해도 어댑터 코드의 나머지 부분은 크게 변경되지 않으며 데이터 목록을 조작하여 헤더 로직을 추가할 수 있습니다. 반면 헤더의 색인을 확인하여 다른 ViewHolder를 사용하면 헤더 레이아웃을 더 자유롭게 설정할 수 있습니다. 또한 어댑터가 지원 데이터를 수정하지 않고 데이터가 뷰에 적응하는 방식을 처리할 수 있습니다.

이 Codelab에서는 목록 시작 부분에 헤더를 표시하도록 RecyclerView을 업데이트합니다. 이 경우 앱은 헤더에 데이터 항목과 다른 ViewHolder를 사용합니다. 앱은 목록의 색인을 확인하여 사용할 ViewHolder를 결정합니다.

1단계: DataItem 클래스 만들기

항목 유형을 추상화하고 어댑터가 '항목'만 처리하도록 하려면 SleepNight 또는 Header을 나타내는 데이터 홀더 클래스를 만들면 됩니다. 그러면 데이터 세트는 데이터 보유자 항목 목록이 됩니다.

GitHub에서 시작 앱을 가져오거나 이전 Codelab에서 빌드한 SleepTracker 앱을 계속 사용할 수 있습니다.

  1. GitHub에서 RecyclerViewHeaders-Starter 코드를 다운로드합니다. RecyclerViewHeaders-Starter 디렉터리에는 이 Codelab에 필요한 SleepTracker 앱의 시작 버전이 포함되어 있습니다. 원하는 경우 이전 Codelab에서 완료한 앱을 계속 사용할 수도 있습니다.
  2. SleepNightAdapter.kt를 엽니다.
  3. SleepNightListener 클래스 아래 최상위 수준에서 데이터 항목을 나타내는 DataItem라는 sealed 클래스를 정의합니다.

    A sealed 클래스는 닫힌 유형을 정의합니다. 즉, DataItem의 모든 하위 클래스는 이 파일에 정의되어야 합니다. 따라서 컴파일러는 서브클래스 수를 알 수 있습니다. 코드의 다른 부분에서 어댑터를 중단할 수 있는 새로운 유형의 DataItem를 정의할 수는 없습니다.
sealed class DataItem {

 }
  1. DataItem 클래스의 본문 내에서 다양한 유형의 데이터 항목을 나타내는 두 클래스를 정의합니다. 첫 번째는 SleepNight을 래핑하는 SleepNightItem이므로 sleepNight이라는 단일 값을 사용합니다. 봉인된 클래스의 일부가 되도록 하려면 DataItem를 확장하도록 합니다.
data class SleepNightItem(val sleepNight: SleepNight): DataItem()
  1. 두 번째 클래스는 헤더를 나타내는 Header입니다. 헤더에는 실제 데이터가 없으므로 object로 선언할 수 있습니다. 즉, Header 인스턴스는 하나만 존재합니다. DataItem을 확장하도록 합니다.
object Header: DataItem()
  1. DataItem 내에서 클래스 수준에서 id라는 abstract Long 속성을 정의합니다. 어댑터가 DiffUtil를 사용하여 항목이 변경되었는지 여부와 방법을 결정할 때 DiffItemCallback는 각 항목의 ID를 알아야 합니다. SleepNightItemHeader이 추상 속성 id를 재정의해야 하므로 오류가 표시됩니다.
abstract val id: Long
  1. SleepNightItem에서 nightId를 반환하도록 id를 재정의합니다.
override val id = sleepNight.nightId
  1. Header에서 id를 재정의하여 매우 작은 수 (문자 그대로 -2의 63제곱)인 Long.MIN_VALUE를 반환합니다. 따라서 기존 nightId와 충돌하지 않습니다.
override val id = Long.MIN_VALUE
  1. 완성된 코드는 다음과 같아야 하며 앱은 오류 없이 빌드되어야 합니다.
sealed class DataItem {
    abstract val id: Long
    data class SleepNightItem(val sleepNight: SleepNight): DataItem()      {
        override val id = sleepNight.nightId
    }

    object Header: DataItem() {
        override val id = Long.MIN_VALUE
    }
}

2단계: 헤더용 ViewHolder 만들기

  1. TextView를 표시하는 새 레이아웃 리소스 파일 header.xml 에 헤더의 레이아웃을 만듭니다. 이 부분은 흥미롭지 않으므로 코드를 바로 보여드리겠습니다.
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr/textAppearanceLarge"
    android:text="Sleep Results"
    android:padding="8dp" />
  1. "Sleep Results"를 문자열 리소스로 추출하고 header_text이라고 호출합니다.
<string name="header_text">Sleep Results</string>
  1. SleepNightAdapter.ktSleepNightAdapter 내에서 ViewHolder 클래스 위에 새 TextViewHolder 클래스를 만듭니다. 이 클래스는 textview.xml 레이아웃을 확장하고 TextViewHolder 인스턴스를 반환합니다. 이전에도 이 작업을 수행한 적이 있으므로 코드는 다음과 같습니다. ViewR을 가져와야 합니다.
    class TextViewHolder(view: View): RecyclerView.ViewHolder(view) {
        companion object {
            fun from(parent: ViewGroup): TextViewHolder {
                val layoutInflater = LayoutInflater.from(parent.context)
                val view = layoutInflater.inflate(R.layout.header, parent, false)
                return TextViewHolder(view)
            }
        }
    }

3단계: SleepNightAdapter 업데이트

다음으로 SleepNightAdapter 선언을 업데이트해야 합니다. 한 가지 유형의 ViewHolder만 지원하는 대신 모든 유형의 뷰 홀더를 사용할 수 있어야 합니다.

상품 유형 정의

  1. SleepNightAdapter.kt에서 최상위 수준의 import 문 아래와 SleepNightAdapter 위에 뷰 유형에 대한 두 상수를 정의합니다.

    RecyclerView은 각 항목의 뷰 유형을 구분하여 뷰 홀더를 올바르게 할당해야 합니다.
    private val ITEM_VIEW_TYPE_HEADER = 0
    private val ITEM_VIEW_TYPE_ITEM = 1
  1. SleepNightAdapter 내에서 getItemViewType()을 재정의하여 현재 항목의 유형에 따라 올바른 헤더 또는 항목 상수를 반환하는 함수를 만듭니다.
override fun getItemViewType(position: Int): Int {
        return when (getItem(position)) {
            is DataItem.Header -> ITEM_VIEW_TYPE_HEADER
            is DataItem.SleepNightItem -> ITEM_VIEW_TYPE_ITEM
        }
    }

SleepNightAdapter 정의 업데이트

  1. SleepNightAdapter 정의에서 ListAdapter의 첫 번째 인수를 SleepNight에서 DataItem로 업데이트합니다.
  2. SleepNightAdapter 정의에서 ListAdapter의 두 번째 일반 인수(SleepNightAdapter.ViewHolder)를 RecyclerView.ViewHolder로 변경합니다. 필수 업데이트에 관한 오류가 표시되며 클래스 헤더는 아래와 같이 표시됩니다.
class SleepNightAdapter(val clickListener: SleepNightListener):
       ListAdapter<DataItem, RecyclerView.ViewHolder>(SleepNightDiffCallback()) {

onCreateViewHolder() 업데이트

  1. RecyclerView.ViewHolder을 반환하도록 onCreateViewHolder()의 서명을 변경합니다.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder
  1. 각 항목 유형에 적절한 뷰 홀더를 테스트하고 반환하도록 onCreateViewHolder() 메서드의 구현을 확장합니다. 업데이트된 메서드는 아래 코드와 같이 표시됩니다.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return when (viewType) {
            ITEM_VIEW_TYPE_HEADER -> TextViewHolder.from(parent)
            ITEM_VIEW_TYPE_ITEM -> ViewHolder.from(parent)
            else -> throw ClassCastException("Unknown viewType ${viewType}")
        }
    }

onBindViewHolder() 업데이트

  1. onBindViewHolder()의 매개변수 유형을 ViewHolder에서 RecyclerView.ViewHolder로 변경합니다.
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int)
  1. 홀더가 ViewHolder인 경우에만 뷰 홀더에 데이터를 할당하는 조건을 추가합니다.
        when (holder) {
            is ViewHolder -> {...}
  1. getItem()에서 반환된 객체 유형을 DataItem.SleepNightItem로 변환합니다. 완성된 onBindViewHolder() 함수는 다음과 같습니다.
  override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (holder) {
            is ViewHolder -> {
                val nightItem = getItem(position) as DataItem.SleepNightItem
                holder.bind(nightItem.sleepNight, clickListener)
            }
        }
    }

diffUtil 콜백 업데이트

  1. SleepNight 대신 새 DataItem 클래스를 사용하도록 SleepNightDiffCallback의 메서드를 변경합니다. 아래 코드와 같이 린트 경고를 억제합니다.
class SleepNightDiffCallback : DiffUtil.ItemCallback<DataItem>() {
    override fun areItemsTheSame(oldItem: DataItem, newItem: DataItem): Boolean {
        return oldItem.id == newItem.id
    }
    @SuppressLint("DiffUtilEquals")
    override fun areContentsTheSame(oldItem: DataItem, newItem: DataItem): Boolean {
        return oldItem == newItem
    }
}

헤더 추가 및 제출

  1. SleepNightAdapter 내에서 onCreateViewHolder() 아래에 다음과 같이 addHeaderAndSubmitList() 함수를 정의합니다. 이 함수는 SleepNight 목록을 사용합니다. ListAdapter에서 제공하는 submitList()를 사용하여 목록을 제출하는 대신 이 함수를 사용하여 헤더를 추가한 다음 목록을 제출합니다.
fun addHeaderAndSubmitList(list: List<SleepNight>?) {}
  1. addHeaderAndSubmitList() 내에서 전달된 목록이 null이면 헤더만 반환하고, 그렇지 않으면 헤더를 목록의 헤드에 연결한 다음 목록을 제출합니다.
val items = when (list) {
                null -> listOf(DataItem.Header)
                else -> listOf(DataItem.Header) + list.map { DataItem.SleepNightItem(it) }
            }
submitList(items)
  1. SleepTrackerFragment.kt를 열고 submitList() 호출을 addHeaderAndSubmitList()로 변경합니다.
  1. 앱을 실행하고 헤더가 수면 항목 목록의 첫 번째 항목으로 표시되는지 확인합니다.

이 앱에는 수정해야 할 사항이 두 가지 있습니다. 하나는 표시되고 하나는 표시되지 않습니다.

  • 헤더가 왼쪽 상단에 표시되어 쉽게 구분할 수 없습니다.
  • 헤더가 하나인 짧은 목록에서는 큰 문제가 없지만 UI 스레드의 addHeaderAndSubmitList()에서 목록 조작을 하면 안 됩니다. 수백 개의 항목, 여러 헤더, 항목을 삽입해야 하는 위치를 결정하는 로직이 있는 목록을 상상해 보세요. 이 작업은 코루틴에 속합니다.

코루틴을 사용하도록 addHeaderAndSubmitList()를 변경합니다.

  1. SleepNightAdapter 클래스 내의 최상위 수준에서 Dispatchers.Default을 사용하여 CoroutineScope을 정의합니다.
private val adapterScope = CoroutineScope(Dispatchers.Default)
  1. addHeaderAndSubmitList()에서 adapterScope의 코루틴을 실행하여 목록을 조작합니다. 그런 다음 아래 코드와 같이 Dispatchers.Main 컨텍스트로 전환하여 목록을 제출합니다.
 fun addHeaderAndSubmitList(list: List<SleepNight>?) {
        adapterScope.launch {
            val items = when (list) {
                null -> listOf(DataItem.Header)
                else -> listOf(DataItem.Header) + list.map { DataItem.SleepNightItem(it) }
            }
            withContext(Dispatchers.Main) {
                submitList(items)
            }
        }
    }
  1. 코드가 빌드되고 실행되며 차이가 없습니다.

현재 헤더는 그리드의 다른 항목과 너비가 동일하며 가로 및 세로로 하나의 스팬을 차지합니다. 전체 그리드에는 스팬 너비가 1인 항목이 가로로 3개 들어가므로 헤더는 가로로 스팬 3개를 사용해야 합니다.

헤더 너비를 수정하려면 모든 열에 걸쳐 데이터를 표시할 시점을 GridLayoutManager에 알려야 합니다. GridLayoutManager에서 SpanSizeLookup를 구성하면 됩니다. 이는 GridLayoutManager가 목록의 각 항목에 사용할 스팬 수를 결정하는 데 사용하는 구성 객체입니다.

  1. SleepTrackerFragment.kt를 엽니다.
  2. onCreateView() 끝부분에서 manager를 정의하는 코드를 찾습니다.
val manager = GridLayoutManager(activity, 3)
  1. manager 아래에 표시된 대로 manager.spanSizeLookup를 정의합니다. setSpanSizeLookup은 람다를 사용하지 않으므로 object을 만들어야 합니다. Kotlin에서 object를 만들려면 object : classname(이 경우 GridLayoutManager.SpanSizeLookup)를 입력합니다.
manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
}
  1. 생성자를 호출하는 컴파일러 오류가 발생할 수 있습니다. 이 경우 Option+Enter (Mac) 또는 Alt+Enter (Windows)로 의도 메뉴를 열어 생성자 호출을 적용합니다.
  1. 그러면 메서드를 재정의해야 한다는 object 오류가 표시됩니다. 커서를 object에 놓고 Option+Enter (Mac) 또는 Alt+Enter (Windows)를 눌러 의도 메뉴를 연 다음 getSpanSize() 메서드를 재정의합니다.
  1. getSpanSize() 본문에서 각 위치에 맞는 범위 크기를 반환합니다. 위치 0의 스팬 크기는 3이고 다른 위치의 스팬 크기는 1입니다. 완성된 코드는 다음과 같습니다.
    manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
            override fun getSpanSize(position: Int) =  when (position) {
                0 -> 3
                else -> 1
            }
        }
  1. 헤더의 모양을 개선하려면 header.xml을 열고 레이아웃 파일 header.xml에 이 코드를 추가합니다.
android:textColor="@color/white_text_color"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:background="@color/colorAccent"
  1. 앱을 실행합니다. 앱은 아래 스크린샷과 같이 표시됩니다.

축하합니다. 이제 작업이 끝났습니다.

Android 스튜디오 프로젝트: RecyclerViewHeaders

  • 헤더는 일반적으로 목록의 너비에 걸쳐 있으며 제목이나 구분 기호 역할을 하는 항목입니다. 목록에는 항목 콘텐츠를 설명하는 단일 헤더가 있거나 항목을 그룹화하고 항목을 서로 구분하는 여러 헤더가 있을 수 있습니다.
  • RecyclerView는 여러 뷰 홀더를 사용하여 이질적인 항목 집합(예: 헤더 및 목록 항목)을 수용할 수 있습니다.
  • 헤더를 추가하는 한 가지 방법은 헤더를 표시해야 하는 색인을 확인하여 다른 ViewHolder를 사용하도록 어댑터를 수정하는 것입니다. Adapter는 헤더를 추적하는 역할을 합니다.
  • 헤더를 추가하는 또 다른 방법은 데이터 그리드의 지원 데이터 세트 (목록)를 수정하는 것입니다. 이 Codelab에서 수행한 작업이 바로 이 방법입니다.

다음은 헤더를 추가하는 주요 단계입니다.

  • 헤더나 데이터를 보유할 수 있는 DataItem를 만들어 목록의 데이터를 추상화합니다.
  • 어댑터에서 헤더의 레이아웃으로 뷰 홀더를 만듭니다.
  • 모든 종류의 RecyclerView.ViewHolder를 사용하도록 어댑터와 메서드를 업데이트합니다.
  • onCreateViewHolder()에서 데이터 항목에 맞는 올바른 유형의 뷰 홀더를 반환합니다.
  • DataItem 클래스를 사용하도록 SleepNightDiffCallback 업데이트
  • 코루틴을 사용하여 데이터 세트에 헤더를 추가한 다음 submitList()를 호출하는 addHeaderAndSubmitList() 함수를 만듭니다.
  • GridLayoutManager.SpanSizeLookup()를 구현하여 헤더가 3개의 스팬 너비만 되도록 합니다.

Udacity 과정:

Android 개발자 문서:

이 섹션에는 강사가 진행하는 과정의 일부로 이 Codelab을 진행하는 학생에게 출제할 수 있는 과제가 나열되어 있습니다. 다음 작업은 강사가 결정합니다.

  • 필요한 경우 과제를 할당합니다.
  • 과제 제출 방법을 학생에게 알립니다.
  • 과제를 채점합니다.

강사는 이러한 추천을 원하는 만큼 사용할 수 있으며 적절하다고 생각되는 다른 과제를 출제해도 됩니다.

이 Codelab을 직접 진행하는 경우 이러한 과제를 자유롭게 사용하여 배운 내용을 테스트해 보세요.

질문에 답하세요

질문 1

ViewHolder에 관한 다음 설명 중 참인 것은 무엇인가요?

▢ 어댑터는 여러 개의 ViewHolder 클래스를 사용하여 헤더와 다양한 유형의 데이터를 보유할 수 있습니다.

▢ 데이터를 위한 뷰 홀더와 헤더를 위한 뷰 홀더를 정확히 하나씩 가질 수 있습니다.

RecyclerView는 여러 유형의 헤더를 지원하지만 데이터가 균일해야 합니다.

▢ 헤더를 추가할 때 올바른 위치에 헤더를 삽입하기 위해 RecyclerView를 서브클래스로 만듭니다.

질문 2

RecyclerView와 함께 코루틴을 사용해야 하는 경우는 언제인가요? 참인 문장을 모두 선택하세요.

▢ 사용 안함 RecyclerView은 UI 요소이므로 코루틴을 사용하면 안 됩니다.

▢ UI 속도를 저하시킬 수 있는 장기 실행 작업에 코루틴을 사용합니다.

▢ 목록 조작은 시간이 오래 걸릴 수 있으므로 항상 코루틴을 사용하여 실행해야 합니다.

▢ 정지 함수가 있는 코루틴을 사용하여 기본 스레드가 차단되지 않도록 합니다.

질문 3

두 개 이상의 ViewHolder을 사용할 때 하지 않아도 되는 것은 무엇인가요?

ViewHolder에서 필요에 따라 확장할 여러 레이아웃 파일을 제공합니다.

onCreateViewHolder()에서 데이터 항목에 맞는 올바른 유형의 뷰 홀더를 반환합니다.

onBindViewHolder()에서 뷰 홀더가 데이터 항목에 맞는 올바른 유형의 뷰 홀더인 경우에만 데이터를 바인딩합니다.

▢ 모든 RecyclerView.ViewHolder를 허용하도록 어댑터 클래스 서명을 일반화합니다.

다음 강의 시작: 8.1 인터넷에서 데이터 가져오기

이 과정의 다른 Codelab 링크는 Android Kotlin 기초 Codelab 방문 페이지를 참고하세요.