Android Kotlin 기초 07.2: RecyclerView를 사용한 DiffUtil 및 데이터 결합

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

소개

이전 Codelab에서는 RecyclerView에 수면의 질에 관한 데이터를 표시하도록 TrackMySleepQuality 앱을 업데이트했습니다. 첫 번째 RecyclerView를 빌드할 때 배운 기술은 너무 크지 않은 간단한 목록을 표시하는 대부분의 RecyclerViews에 충분합니다. 하지만 대규모 목록에서 RecyclerView를 더 효율적으로 만들고 복잡한 목록과 그리드에서 코드를 더 쉽게 유지 관리하고 확장할 수 있는 여러 기법이 있습니다.

이 Codelab에서는 이전 Codelab의 수면 추적기 앱을 기반으로 빌드합니다. 수면 데이터 목록을 업데이트하는 더 효과적인 방법을 배우고 RecyclerView와 함께 데이터 결합을 사용하는 방법을 알아봅니다. (이전 Codelab의 앱이 없는 경우 이 Codelab의 시작 코드를 다운로드하면 됩니다.)

기본 요건

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

학습할 내용

  • DiffUtil를 사용하여 RecyclerView에 표시된 목록을 효율적으로 업데이트하는 방법
  • RecyclerView와 함께 데이터 결합을 사용하는 방법
  • 결합 어댑터를 사용하여 데이터를 변환하는 방법

실습할 내용

  • 이 시리즈의 이전 Codelab에서 TrackMySleepQuality 앱을 기반으로 빌드합니다.
  • DiffUtil을 사용하여 목록을 효율적으로 업데이트하도록 SleepNightAdapter을 업데이트합니다.
  • 결합 어댑터를 사용하여 데이터를 변환하여 RecyclerView에 데이터 결합을 구현합니다.

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

왼쪽에 표시된 첫 번째 화면에는 추적을 시작하고 중지하는 버튼이 있습니다. 화면에는 사용자의 수면 데이터가 일부 표시됩니다. 지우기 버튼은 앱이 사용자를 위해 수집한 모든 데이터를 완전히 삭제합니다. 오른쪽에 표시된 두 번째 화면은 수면의 질 평가를 선택하는 화면입니다.

이 앱은 UI 컨트롤러(ViewModelLiveData)와 Room 데이터베이스를 사용하여 수면 데이터를 유지하도록 설계되었습니다.

수면 데이터가 RecyclerView에 표시됩니다. 이 Codelab에서는 RecyclerViewDiffUtil 및 데이터 바인딩 부분을 빌드합니다. 이 Codelab을 마치면 앱은 똑같아 보이지만 더 효율적이고 확장 및 유지 관리가 더 쉬워집니다.

이전 Codelab의 SleepTracker 앱을 계속 사용하거나 GitHub에서 RecyclerViewDiffUtilDataBinding-Starter 앱을 다운로드할 수 있습니다.

  1. 필요한 경우 GitHub에서 RecyclerViewDiffUtilDataBinding-Starter 앱을 다운로드하고 Android 스튜디오에서 프로젝트를 엽니다.
  2. 앱을 실행합니다.
  3. SleepNightAdapter.kt 파일을 엽니다.
  4. 코드를 검사하여 앱의 구조를 숙지합니다. 어댑터 패턴과 함께 RecyclerView를 사용하여 사용자에게 수면 데이터를 표시하는 방법을 요약한 아래 다이어그램을 참고하세요.

  • 사용자 입력에서 앱은 SleepNight 객체 목록을 만듭니다. 각 SleepNight 객체는 단일 수면 시간, 수면 시간, 수면의 질을 나타냅니다.
  • SleepNightAdapterSleepNight 객체 목록을 RecyclerView에서 사용하고 표시할 수 있는 형식으로 조정합니다.
  • SleepNightAdapter 어댑터는 리사이클러 뷰가 데이터를 표시할 수 있도록 뷰, 데이터, 메타 정보가 포함된 ViewHolders를 생성합니다.
  • RecyclerViewSleepNightAdapter를 사용하여 표시할 항목 수 (getItemCount())를 결정합니다. RecyclerViewonCreateViewHolder()onBindViewHolder()를 사용하여 표시할 데이터에 바인딩된 뷰 홀더를 가져옵니다.

notifyDataSetChanged() 메서드가 비효율적임

목록의 항목이 변경되어 업데이트해야 함을 RecyclerView에 알리기 위해 현재 코드는 아래와 같이 SleepNightAdapter에서 notifyDataSetChanged()를 호출합니다.

var data =  listOf<SleepNight>()
   set(value) {
       field = value
       notifyDataSetChanged()
   }

하지만 notifyDataSetChanged()RecyclerView에게 전체 목록이 유효하지 않을 수 있다고 알려줍니다. 따라서 RecyclerView는 화면에 표시되지 않는 항목을 포함하여 목록의 모든 항목을 다시 바인딩하고 다시 그립니다. 이는 불필요한 작업이 많습니다. 크거나 복잡한 목록의 경우 이 프로세스에 시간이 오래 걸려 사용자가 목록을 스크롤할 때 디스플레이가 깜박이거나 끊길 수 있습니다.

이 문제를 해결하려면 RecyclerView에 변경된 내용을 정확하게 알려주면 됩니다. 그러면 RecyclerView가 화면에서 변경된 뷰만 업데이트할 수 있습니다.

RecyclerView에는 단일 요소를 업데이트하기 위한 다양한 API가 있습니다. notifyItemChanged()를 사용하여 RecyclerView에 항목이 변경되었음을 알릴 수 있으며, 추가, 삭제 또는 이동된 항목에 유사한 함수를 사용할 수 있습니다. 모든 작업을 수동으로 할 수도 있지만 이 작업은 간단하지 않으며 상당한 코드가 필요할 수 있습니다.

다행히 더 나은 방법이 있습니다.

DiffUtil은 효율적이며 어려운 작업을 대신 처리합니다.

RecyclerView에는 두 목록 간의 차이를 계산하는 데 사용되는 DiffUtil이라는 클래스가 있습니다. DiffUtil는 이전 목록과 새 목록을 가져와서 다른 점을 파악합니다. 추가, 삭제 또는 변경된 항목을 찾습니다. 그런 다음 Eugene W. Myers's difference algorithm을 사용하여 이전 목록에서 새 목록을 생성하기 위해 변경해야 하는 최소 횟수를 파악합니다.

DiffUtil가 변경된 항목을 파악하면 RecyclerView는 이 정보를 사용하여 변경, 추가, 삭제 또는 이동된 항목만 업데이트할 수 있으므로 전체 목록을 다시 실행하는 것보다 훨씬 효율적입니다.

이 작업에서는 DiffUtil를 사용하여 데이터 변경사항에 맞게 RecyclerView를 최적화하도록 SleepNightAdapter를 업그레이드합니다.

1단계: SleepNightDiffCallback 구현

DiffUtil 클래스의 기능을 사용하려면 DiffUtil.ItemCallback를 확장하세요.

  1. SleepNightAdapter.kt를 엽니다.
  2. SleepNightAdapter의 전체 클래스 정의 아래에서 DiffUtil.ItemCallback를 확장하는 SleepNightDiffCallback이라는 새 최상위 클래스를 만듭니다. SleepNight를 일반 매개변수로 전달합니다.
class SleepNightDiffCallback : DiffUtil.ItemCallback<SleepNight>() {
}
  1. SleepNightDiffCallback 클래스 이름에 커서를 놓습니다.
  2. Alt+Enter (Mac의 경우 Option+Enter )을 누르고 Implement Members를 선택합니다.
  3. 열린 대화상자에서 Shift 키를 누른 채 왼쪽 클릭하여 areItemsTheSame()areContentsTheSame() 메서드를 선택한 후 OK를 클릭합니다.

    그러면 아래와 같이 두 메서드의 스텁이 SleepNightDiffCallback 내부에 생성됩니다. DiffUtil은 이러한 두 가지 메서드를 사용하여 목록과 항목이 어떻게 변경되었는지 파악합니다.
    override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
  1. areItemsTheSame() 내에서 TODO을 전달된 두 SleepNight 항목(oldItemnewItem)이 동일한지 테스트하는 코드로 바꿉니다. 상품의 nightId이 동일하면 동일한 상품이므로 true을 반환합니다. 그렇지 않으면 false을 반환합니다. DiffUtil은 이 테스트를 사용하여 항목이 추가, 삭제 또는 이동되었는지 확인합니다.
override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
   return oldItem.nightId == newItem.nightId
}
  1. areContentsTheSame() 내에서 oldItemnewItem에 동일한 데이터가 포함되어 있는지, 즉 동일한지 확인합니다. SleepNight는 데이터 클래스이므로 이 동등성 검사는 모든 필드를 확인합니다. Data 클래스는 equals 및 몇 가지 다른 메서드를 자동으로 정의합니다. oldItemnewItem 사이에 차이가 있으면 이 코드는 DiffUtil에 항목이 업데이트되었음을 알립니다.
override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
   return oldItem == newItem
}

RecyclerView를 사용하여 변경되는 목록을 표시하는 것이 일반적인 패턴입니다. RecyclerView는 목록으로 지원되는 RecyclerView 어댑터를 빌드하는 데 도움이 되는 어댑터 클래스 ListAdapter를 제공합니다.

ListAdapter는 목록을 추적하고 목록이 업데이트되면 어댑터에 알립니다.

1단계: 어댑터를 변경하여 ListAdapter 확장

  1. SleepNightAdapter.kt 파일에서 ListAdapter를 확장하도록 SleepNightAdapter의 클래스 서명을 변경합니다.
  2. 메시지가 표시되면 androidx.recyclerview.widget.ListAdapter를 가져옵니다.
  3. SleepNightAdapter.ViewHolder 앞에 ListAdapterSleepNight를 첫 번째 인수로 추가합니다.
  4. 생성자에 SleepNightDiffCallback()을 매개변수로 추가합니다. ListAdapter는 이를 사용하여 목록에서 변경된 내용을 파악합니다. 완성된 SleepNightAdapter 클래스 서명은 아래와 같이 표시됩니다.
class SleepNightAdapter : ListAdapter<SleepNight, SleepNightAdapter.ViewHolder>(SleepNightDiffCallback()) {
  1. SleepNightAdapter 클래스 내에서 setter를 포함한 data 필드를 삭제합니다. ListAdapter에서 목록을 추적하므로 더 이상 필요하지 않습니다.
  2. ListAdapter이 이 메서드를 구현하므로 getItemCount()의 재정의를 삭제합니다.
  3. onBindViewHolder()의 오류를 없애려면 item 변수를 변경하세요. data를 사용하여 item를 가져오는 대신 ListAdapter에서 제공하는 getItem(position) 메서드를 호출합니다.
val item = getItem(position)

2단계: submitList()를 사용하여 목록을 업데이트된 상태로 유지

변경된 목록을 사용할 수 있을 때 코드가 ListAdapter에 알려야 합니다. ListAdapter는 새 버전의 목록을 사용할 수 있음을 ListAdapter에 알리는 submitList()이라는 메서드를 제공합니다. 이 메서드가 호출되면 ListAdapter는 새 목록과 이전 목록을 비교하여 추가, 삭제, 이동 또는 변경된 항목을 감지합니다. 그러면 ListAdapterRecyclerView에 표시된 항목을 업데이트합니다.

  1. SleepTrackerFragment.kt를 엽니다.
  2. onCreateView()에서 sleepTrackerViewModel의 관찰자에서 삭제한 data 변수가 참조되는 오류를 찾습니다.
  3. adapter.data = itadapter.submitList(it) 호출로 바꿉니다. 업데이트된 코드는 아래와 같습니다.

sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
   it?.let {
       adapter.submitList(it)
   }
})
  1. 앱을 실행합니다. 목록이 작으면 눈에 띄지 않을 수 있지만 더 빠르게 실행됩니다.

이 작업에서는 이전 Codelab과 동일한 기법을 사용하여 데이터 바인딩을 설정하고 findViewById() 호출을 삭제합니다.

1단계: 레이아웃 파일에 데이터 결합 추가

  1. 텍스트 탭에서 list_item_sleep_night.xml 레이아웃 파일을 엽니다.
  2. ConstraintLayout 태그에 커서를 놓고 Alt+Enter (Mac의 경우 Option+Enter )를 누릅니다. 의도 메뉴('빠른 수정' 메뉴)가 열립니다.
  3. 데이터 결합 레이아웃으로 변환을 선택합니다. 이렇게 하면 레이아웃이 <layout>로 래핑되고 내부에 <data> 태그가 추가됩니다.
  4. 필요한 경우 상단으로 스크롤하고 <data> 태그 내에서 sleep이라는 변수를 선언합니다.
  5. typeSleepNight, com.example.android.trackmysleepquality.database.SleepNight의 정규화된 이름으로 만듭니다. 완성된 <data> 태그는 아래와 같이 표시됩니다.
   <data>
        <variable
            name="sleep"
            type="com.example.android.trackmysleepquality.database.SleepNight"/>
    </data>
  1. Binding 객체의 생성을 강제하려면 Build > Clean Project를 선택한 다음 Build > Rebuild Project를 선택합니다. (그래도 문제가 해결되지 않으면 File > Invalidate Caches / Restart를 선택하세요.) ListItemSleepNightBinding 바인딩 객체와 관련 코드가 프로젝트의 생성된 파일에 추가됩니다.

2단계: 데이터 결합을 사용하여 항목 레이아웃 확장

  1. SleepNightAdapter.kt를 엽니다.
  2. ViewHolder 클래스에서 from() 메서드를 찾습니다.
  3. view 변수의 선언을 삭제합니다.

삭제할 코드:

val view = layoutInflater
       .inflate(R.layout.list_item_sleep_night, parent, false)
  1. view 변수가 있던 위치에 ListItemSleepNightBinding 바인딩 객체를 인플레이션하는 binding이라는 새 변수를 정의합니다(아래 참고). 바인딩 객체를 필요한 대로 가져옵니다.
val binding =
ListItemSleepNightBinding.inflate(layoutInflater, parent, false)
  1. 함수 끝에서 view를 반환하는 대신 binding를 반환합니다.
return ViewHolder(binding)
  1. 이 오류를 없애려면 binding 단어에 커서를 놓습니다. Alt+Enter (Mac의 경우 Option+Enter)를 눌러 의도 메뉴를 엽니다.
  1. 클래스 'ViewHolder'의 기본 생성자의 'itemView' 유형을 'ListItemSleepNightBinding'으로 변경을 선택합니다. 이렇게 하면 ViewHolder 클래스의 매개변수 유형이 업데이트됩니다.

  1. 위로 스크롤하여 ViewHolder의 클래스 정의를 확인하여 서명의 변경사항을 확인합니다. from() 메서드에서 itemViewbinding로 변경했기 때문에 itemView에 오류가 표시됩니다.

    ViewHolder 클래스 정의에서 itemView이 나타나는 위치 중 하나를 마우스 오른쪽 버튼으로 클릭하고 리팩터링 > 이름 바꾸기를 선택합니다. 이름을 binding로 변경합니다.
  2. 생성자 매개변수 bindingval을 접두사로 추가하여 속성으로 만듭니다.
  3. 부모 클래스 RecyclerView.ViewHolder 호출에서 매개변수를 binding에서 binding.root로 변경합니다. View를 전달해야 하며 binding.root은 항목 레이아웃의 루트 ConstraintLayout입니다.
  4. 완성된 클래스 선언은 아래 코드와 같이 표시됩니다.
class ViewHolder private constructor(val binding: ListItemSleepNightBinding) : RecyclerView.ViewHolder(binding.root){

findViewById() 호출에 관한 오류도 표시되므로 다음으로 이 오류를 수정합니다.

3단계: findViewById() 대체

이제 findViewById() 대신 binding 객체를 사용하도록 sleepLength, quality, qualityImage 속성을 업데이트할 수 있습니다.

  1. 아래와 같이 binding 객체의 뷰를 사용하도록 sleepLength, qualityString, qualityImage의 초기화를 변경합니다. 이후에는 코드에 오류가 더 이상 표시되지 않습니다.
val sleepLength: TextView = binding.sleepLength
val quality: TextView = binding.qualityString
val qualityImage: ImageView = binding.qualityImage

바인딩 객체가 있으면 sleepLength, quality, qualityImage 속성을 더 이상 정의하지 않아도 됩니다. DataBinding는 조회를 캐시하므로 이러한 속성을 선언할 필요가 없습니다.

  1. sleepLength, quality, qualityImage 속성 이름을 마우스 오른쪽 버튼으로 클릭합니다. 리팩터링 > 인라인을 선택하거나 Control+Command+N (Mac의 경우 Option+Command+N)를 누릅니다.
  2. 앱을 실행합니다. 오류가 있는 경우 프로젝트를 정리하고 다시 빌드해야 할 수 있습니다.

이 작업에서는 결합 어댑터와 함께 데이터 결합을 사용하여 뷰에 데이터를 설정하도록 앱을 업그레이드합니다.

이전 Codelab에서는 Transformations 클래스를 사용하여 LiveData를 가져오고 텍스트 뷰에 표시할 서식이 지정된 문자열을 생성했습니다. 하지만 다른 유형이나 복잡한 유형을 바인딩해야 하는 경우 데이터 바인딩이 이러한 유형을 사용할 수 있도록 바인딩 어댑터를 제공할 수 있습니다. 결합 어댑터는 데이터를 가져와 데이터 결합이 텍스트나 이미지와 같은 뷰를 결합하는 데 사용할 수 있는 형식으로 조정하는 어댑터입니다.

품질 이미지용 바인딩 어댑터 하나와 각 텍스트 필드용 바인딩 어댑터 하나씩 총 세 개의 바인딩 어댑터를 구현합니다. 요약하자면 결합 어댑터를 선언하려면 항목과 뷰를 사용하는 메서드를 정의하고 @BindingAdapter로 주석을 답니다. 메서드의 본문에서 변환을 구현합니다. Kotlin에서는 데이터를 수신하는 뷰 클래스에서 확장 함수로 바인딩 어댑터를 작성할 수 있습니다.

1단계: 바인딩 어댑터 만들기

이 단계에서는 여러 클래스를 가져와야 하며 개별적으로 호출되지 않습니다.

  1. SleepNightAdapater.kt를 엽니다.
  2. ViewHolder 클래스 내에서 bind() 메서드를 찾아 이 메서드의 기능을 다시 확인합니다. binding.sleepLength, binding.quality, binding.qualityImage 값을 계산하는 코드를 가져와 어댑터 내에서 대신 사용합니다. (지금은 코드를 그대로 두세요. 나중에 이동합니다.)
  3. sleeptracker 패키지에서 BindingUtils.kt이라는 파일을 만들어 엽니다.
  4. TextView에서 setSleepDurationFormatted이라는 확장 함수를 선언하고 SleepNight를 전달합니다. 이 함수는 수면 시간을 계산하고 형식을 지정하는 어댑터가 됩니다.
fun TextView.setSleepDurationFormatted(item: SleepNight) {}
  1. setSleepDurationFormatted 본문에서 ViewHolder.bind()에서 한 것처럼 데이터를 뷰에 바인딩합니다. convertDurationToFormatted()를 호출한 다음 TextViewtext을 형식이 지정된 텍스트로 설정합니다. (TextView의 확장 함수이므로 text 속성에 직접 액세스할 수 있습니다.)
text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, context.resources)
  1. 이 결합 어댑터에 관해 데이터 결합에 알리려면 함수에 @BindingAdapter 주석을 추가합니다.
  2. 이 함수는 sleepDurationFormatted 속성의 어댑터이므로 sleepDurationFormatted@BindingAdapter에 인수로 전달합니다.
@BindingAdapter("sleepDurationFormatted")
  1. 두 번째 어댑터는 SleepNight 객체의 값을 기반으로 수면 효율을 설정합니다. TextView에서 setSleepQualityString()라는 확장 함수를 만들고 SleepNight를 전달합니다.
  2. 본문에서 ViewHolder.bind()에서와 같이 데이터를 뷰에 바인딩합니다. convertNumericQualityToString를 호출하고 text를 설정합니다.
  3. 함수에 @BindingAdapter("sleepQualityString") 주석을 추가합니다.
@BindingAdapter("sleepQualityString")
fun TextView.setSleepQualityString(item: SleepNight) {
   text = convertNumericQualityToString(item.sleepQuality, context.resources)
}
  1. 세 번째 결합 어댑터는 이미지 뷰에 이미지를 설정합니다. ImageView에서 확장 함수를 만들고 setSleepImage를 호출하고 ViewHolder.bind()의 코드를 사용합니다(아래 참고).
@BindingAdapter("sleepImage")
fun ImageView.setSleepImage(item: SleepNight) {
   setImageResource(when (item.sleepQuality) {
       0 -> R.drawable.ic_sleep_0
       1 -> R.drawable.ic_sleep_1
       2 -> R.drawable.ic_sleep_2
       3 -> R.drawable.ic_sleep_3
       4 -> R.drawable.ic_sleep_4
       5 -> R.drawable.ic_sleep_5
       else -> R.drawable.ic_sleep_active
   })
}

2단계: SleepNightAdapter 업데이트

  1. SleepNightAdapter.kt를 엽니다.
  2. 이제 데이터 바인딩과 새 어댑터를 사용하여 이 작업을 수행할 수 있으므로 bind() 메서드의 모든 항목을 삭제합니다.
fun bind(item: SleepNight) {
}
  1. bind() 내에서 item에 절전을 할당합니다. 바인딩 객체에 새 SleepNight에 관해 알려야 하기 때문입니다.
binding.sleep = item
  1. 이 줄 아래에 binding.executePendingBindings()를 추가합니다. 이 호출은 데이터 바인딩이 대기 중인 바인딩을 즉시 실행하도록 요청하는 최적화입니다. RecyclerView에서 바인딩 어댑터를 사용할 때는 항상 executePendingBindings()를 호출하는 것이 좋습니다. 뷰의 크기 조정을 약간 빠르게 할 수 있기 때문입니다.
 binding.executePendingBindings()

3단계: XML 레이아웃에 바인딩 추가

  1. list_item_sleep_night.xml를 엽니다.
  2. ImageView에서 이미지를 설정하는 결합 어댑터와 이름이 동일한 app 속성을 추가합니다. 아래와 같이 sleep 변수를 전달합니다.

    이 속성은 어댑터를 통해 뷰와 바인딩 객체 간 연결을 만듭니다. sleepImage가 참조될 때마다 어댑터는 SleepNight의 데이터를 적응시킵니다.
app:sleepImage="@{sleep}"
  1. sleep_lengthquality_string 텍스트 뷰에도 동일한 작업을 실행합니다. sleepDurationFormatted 또는 sleepQualityString가 참조될 때마다 어댑터는 SleepNight의 데이터를 적응시킵니다.
app:sleepDurationFormatted="@{sleep}"
app:sleepQualityString="@{sleep}"
  1. 앱을 실행합니다. 이전과 정확히 동일하게 작동합니다. 바인딩 어댑터는 데이터가 변경될 때 뷰의 형식을 지정하고 업데이트하는 모든 작업을 처리하여 ViewHolder를 간소화하고 이전보다 훨씬 나은 코드 구조를 제공합니다.

마지막 몇 가지 운동에서 동일한 목록을 표시했습니다. 이는 Adapter 인터페이스를 사용하면 다양한 방식으로 코드를 설계할 수 있음을 보여주기 위한 것입니다. 코드가 복잡할수록 아키텍처를 잘 설계하는 것이 중요해집니다. 프로덕션 앱에서는 이러한 패턴과 기타 패턴이 RecyclerView와 함께 사용됩니다. 이러한 패턴은 모두 작동하며 각각 장점이 있습니다. 어떤 것을 선택할지는 빌드하는 항목에 따라 다릅니다.

축하합니다. 이제 Android에서 RecyclerView를 마스터하기 위한 과정을 잘 밟아가고 계십니다.

Android 스튜디오 프로젝트: RecyclerViewDiffUtilDataBinding

DiffUtil:

  • RecyclerView에는 두 목록 간의 차이를 계산하는 데 사용되는 DiffUtil이라는 클래스가 있습니다.
  • DiffUtil에는 두 목록 간의 차이를 파악하기 위해 확장하는 ItemCallBack이라는 클래스가 있습니다.
  • ItemCallback 클래스에서 areItemsTheSame() 메서드와 areContentsTheSame() 메서드를 재정의해야 합니다.

ListAdapter:

  • 목록 관리를 무료로 받으려면 RecyclerView.Adapter 대신 ListAdapter 클래스를 사용하면 됩니다. 하지만 ListAdapter를 사용하는 경우 다른 레이아웃의 어댑터를 직접 작성해야 하므로 이 Codelab에서는 방법을 보여줍니다.
  • Android 스튜디오에서 의도 메뉴를 열려면 코드 항목에 커서를 놓고 Alt+Enter (Mac의 경우 Option+Enter)를 누릅니다. 이 메뉴는 특히 코드를 리팩터링하고 메서드를 구현하기 위한 스텁을 만드는 데 유용합니다. 메뉴는 상황에 따라 달라지므로 올바른 메뉴를 표시하려면 커서를 정확하게 배치해야 합니다.

데이터 결합:

  • 항목 레이아웃에서 데이터 결합을 사용하여 데이터를 뷰에 결합합니다.

결합 어댑터:

  • 이전에는 Transformations를 사용하여 데이터에서 문자열을 만들었습니다. 다양하거나 복잡한 유형의 데이터를 바인딩해야 하는 경우 데이터 바인딩이 이를 사용할 수 있도록 바인딩 어댑터를 제공하세요.
  • 결합 어댑터를 선언하려면 항목과 뷰를 사용하는 메서드를 정의하고 메서드에 @BindingAdapter 주석을 추가합니다. Kotlin에서는 View의 확장 함수로 바인딩 어댑터를 작성할 수 있습니다. 어댑터가 적응하는 속성의 이름을 전달합니다. 예를 들면 다음과 같습니다.
@BindingAdapter("sleepDurationFormatted")
  • XML 레이아웃에서 바인딩 어댑터와 이름이 같은 app 속성을 설정합니다. 데이터가 포함된 변수를 전달합니다. 예를 들면 다음과 같습니다.
.app:sleepDurationFormatted="@{sleep}"

Udacity 과정:

Android 개발자 문서:

기타 자료:

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

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

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

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

질문에 답하세요

질문 1

다음 중 DiffUtil을 사용하는 데 필요한 것은 무엇인가요? 해당하는 항목을 모두 선택해 주세요.

ItemCallBack 클래스를 확장합니다.

areItemsTheSame() 재정의

areContentsTheSame() 재정의

▢ 데이터 결합을 사용하여 항목 간의 차이점을 추적합니다.

질문 2

결합 어댑터에 관한 다음 설명 중 참인 것은 무엇인가요?

▢ 바인딩 어댑터는 @BindingAdapter로 주석 처리된 함수입니다.

▢ 바인딩 어댑터를 사용하면 뷰 홀더로부터 데이터 형식을 분리할 수 있습니다.

▢ 결합 어댑터를 사용하려면 RecyclerViewAdapter를 사용해야 합니다.

▢ 복잡한 데이터를 변환해야 하는 경우 결합 어댑터는 좋은 솔루션이 됩니다.

질문 3

언제 결합 어댑터 대신 Transformations를 사용하는 것이 좋을까요? 해당하는 항목을 모두 선택해 주세요.

▢ 데이터가 간단합니다.

▢ 문자열의 형식을 지정하고 있습니다.

▢ 목록이 너무 깁니다.

ViewHolder에 뷰가 하나만 포함되어 있습니다.

다음 강의 시작: 7.3: RecyclerView를 사용한 GridLayout