Udacity Developing Android Apps with Kotlin 레슨7 RecyclerView 요약

Lesson 7: RecyclerView

RecyclerView


1. Recycle Woman

2. Introduction

3. Your first RecyclerView

Room+ViewModel과 Adapter의 연결 구조

ViewHolder의 역할

4. Exercise: Add a RecyclerView

5. Exercise: Display SleepQuality Data

프래그먼트에서 db 데이터를 observe 하다가 데이터가 갱신되면 adapter.data에 대입해주는 부분이 있음.

6. Exercise: Recycling ViewHolders

7. Displaying Sleep Quality

ViewHolder에는 많은 기능이 있다. 활용하자.

  • getAdapterPosition()
  • getLayoutPosition()
  • getItemId()

8. Exercise: Display the SleepQuality List

9. Exercise: Refactor onBindViewHolder

onBindViewHolder 로직을 ViewHolder의 bind 메소드로 이동

10. Exercise: Refactor onCreateViewHolder

onCreateViewHolder 로직을 ViewHolder의 from 메소드로 이동

11. Improving Data Refresh

RecyclerView의 Rich API를 활용하기에 딱 좋은 DiffUtil

DiffUtil의 이점

12. Exercise: Refresh Data with DiffUtil

class SleepNightDiffCallback : DiffUtil.ItemCallback<SleepNight>() {
  override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
      return oldItem.nightId == newItem.nightId
  }

  override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
      return oldItem == newItem // data 클래스이기에 비교 가능!
  }
}

...

adapter.submitList() // DiffUtil을 사용해 리스트를 갱신한다.

Data Binding 더하기


13. Exercise: Add DataBinding to the Adapter

fun from(parent: ViewGroup): ViewHolder {
	val layoutInflater = LayoutInflater.from(parent.context)
	val binding = ListItemSleepNightBinding.inflate(layoutInflater, parent, false)
	return ViewHolder(binding)
}

class ViewHolder private constructor(val binding: ListItemSleepNightBinding):
	RecyclerView.ViewHolder(binding.root) {
}

14. Exercise: Add Binding Adapters

@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
   })
}

코틀린 확장함수를 활용한 바인딩 메소드

binding.sleep = item
binding.executePendingBindings() // 바인딩 어댑터를 적용시키기

<ImageView
  android:id="@+id/quality_image"
  android:layout_width="@dimen/icon_size"
  android:layout_height="60dp"
  android:layout_marginStart="16dp"
  android:layout_marginTop="8dp"
  android:layout_marginBottom="8dp"
  app:layout_constraintBottom_toBottomOf="parent"
  app:layout_constraintStart_toStartOf="parent"
  app:layout_constraintTop_toTopOf="parent"
  tools:srcCompat="@drawable/ic_sleep_5"
  app:sleepImage="@{sleep}"/> <!--요기-->

바인딩 어댑터를 썼을 때와 그냥 메소드를 썼을 때의 차이는?

바인딩 어댑터는 static 메소드라고 보면 됨.

15. Finishing Your First RecyclerView

RecyclerView를 사용하는 구조 정리

16. Googler Interview: Romain Guy and Chet Haase

17. Using GridLayout

18. Exercise: Change LinearLayout to GridLayout

19. Interacting with List Items

20. Exercise: Implement a Click Listener

<variable
     name="clickListener" type="com.example.android.trackmysleepquality.sleeptracker.SleepNightListener"/>

21. Exercise: Navigate on Click

22. Adding Headers to the RecyclerView

23. Extra Credit: Add a List Header

sealed class DataItem {
     data class SleepNightItem(val sleepNight: SleepNight): DataItem() {
         override val id = sleepNight.nightId
     }

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

     abstract val id: Long
 }

sealed?

enum 클래스의 확장

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)
        }
    }
}

24. Headers in GridLayout

25. Extra Credit: Add a Header to the GridLayout

26. Summary

마치면서

이 레슨에서도 Room~LiveData 자동 갱신 연계가 인상적이었다. DB data Item이 추가 되면, DB data list가 갱신되는 것.

페이징 라이브러리, Room, LiveData 연계 더 정확히 알아보기

그리고 View 이벤트 리스닝의 흐름도 명확해서 좋았다. Fragment가 Adapter를 만들 때 생성자로 리스너를 넘겨주고, 리스너는 아이템뷰에 바인딩 된다. View 이벤트가 발생되면 리스너 구현부에서 ViewModel의 메소드를 호출하게 된다.