[Android] RecyclerView의 ListAdapter를 viewBinding과 함께 사용하는 방법

소개



RecyclerView 에서 ViewBinding 과 ListAdapter 를 조합해 사용하는 방법에 대해 조사했으므로 정리합니다.



표시할 데이터 선언



소개 User라는 firstName과 lastName, age를 가진 데이터 클래스를 정의합니다.
이번에는 이 User 에 정의한 데이터를 RecyclerView 로 표시할 수 있도록 합니다.
data class User(val firstName: String, val lastName: String, val age: Int) {
    val id = UUID.randomUUID().toString()
}

표시할 View 선언



RecyclerView에 표시 할 View를 선언하기 전에 buildFeatures에서 viewBinding을 true로 설정하십시오. 이제 viewBinding이 활성화되어 viewBinding을 사용하여 View를 생성할 수 있습니다.
android {
    ︙省略
    buildFeatures {
        viewBinding true
    }
    ︙省略
}

이번에는 View를 layout_user_item.xml이라는 파일에 정의합니다.
아래와 같이 View 로서 firstName 이나 lastName, age 를 표시할 수 있도록 해 둡니다.


<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
    android:layout_margin="8dp"
    android:background="#eeeeee"
    android:padding="8dp">

    <TextView
        android:id="@+id/age_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="64sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/first_name_text_view"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="100" />

    <TextView
        android:id="@+id/first_name_text_view"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:textSize="32sp"
        app:layout_constraintBottom_toTopOf="@id/last_name_text_view"
        app:layout_constraintEnd_toStartOf="@id/age_text_view"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="FIRST NAME" />

    <TextView
        android:id="@+id/last_name_text_view"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:textSize="32sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@id/age_text_view"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/first_name_text_view"
        tools:text="LAST NAME" />
</androidx.constraintlayout.widget.ConstraintLayout>

디스플레이를 제어하는 ​​어댑터 만들기



그런 다음 RecyclerView의 표시를 제어하는 ​​RecyclerView.Adapter를 만듭니다. 이전에는 RecyclerView.Adapter 를 처음부터 구현할 필요가 꽤 힘들었습니다만 지금은 ListAdapter 라고 하는 RecyclerViewAdapter 의 구현을 편하게 해 주는 클래스가 공식적으로 제공되고 있습니다.

List Adapter 에는 다음의 특징이 있어 메리트가 있는 어댑터입니다만 다음과 같이 구현합니다.
  • RecyclerView.Adapter는 RecyclerView에서 데이터 표시를 제어하는 ​​데 필요한 클래스입니다.
  • ReccylerView.Adapter 를 보다 간단하게 구현할 수 있도록 한 것이 ListAdater 라고 하는 클래스
  • 또한 ListAdapter 는 차등 갱신의 구조가 서포트되고 있어, 간단하게 차등 갱신을 구현할 수 있게 되어 있다.
  • /**
     * ListAdapter<A: Object, B: RecyclerView.ViewHolder>(C: DiffUtils.ItemCallback<A>) を継承する
     * 今回は User を表示するので、User を表示するための View を保持した ViewHolder と User の差分を比較するための ItemCallback をセットしてやります。
     * 
     * - A:Object には表示するデータを保持するクラスをセットする
     * - B:RecyclerView.ViewHolder には表示する View を保持する ViewHolder をセットする
     * - C:DiffUtils.ItemCallback<A> には A:Object の差分確認方法を実装した ItemCallback をセットする
     */
    class UserListAdapter : ListAdapter<User, UserItemViewHolder>(DIFF_UTIL_ITEM_CALLBACK) {
        // ここで View を生成する、生成した View は ViewHolder に格納して、戻り値として返す
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserItemViewHolder {
            val view = LayoutUserItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
            return UserItemViewHolder(view)
        }
    
        // その位置の User を取得し、ViewHolder を通じて View に User 情報をセットする
        override fun onBindViewHolder(holderUser: UserItemViewHolder, position: Int) {
            holderUser.bind(getItem(position))
        }
    }
    
    /**
     * 生成した View を保持する、 bind で User を View に反映させる
     */
    class UserItemViewHolder(
        private val binding: LayoutUserItemBinding
    ) : RecyclerView.ViewHolder(binding.root) {
        fun bind(user: User) {
            binding.firstNameTextView.text = user.firstName
            binding.lastNameTextView.text = user.lastName
            binding.ageTextView.text = user.age.toString()
        }
    }
    
    /**
     * User の差分確認する
     */
    val DIFF_UTIL_ITEM_CALLBACK = object : DiffUtil.ItemCallback<User>() {
        // 渡されたデータが同じ値であるか確認をする
        override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
            return oldItem == newItem
        }
    
        // 渡されたデータが同じ項目であるか確認する
        override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
            return oldItem.id == newItem.id
        }
    }
    
    

    RecyclerView에 데이터 표시



    표시하는 데이터, 표시하는 View, 표시를 제어하는 ​​어댑터가 완성되었으므로,
    나머지는 RecyclerView 를 정의해, 작성한 ListAdapter 를 세트 하면 OK입니다.
    그리고 ListAdapter 의 submitList 를 호출하면 세트 한 데이터가 RecyclerView 에 표시됩니다.
    class MainActivity : AppCompatActivity() {
        private lateinit var binding: ActivityMainBinding
        private val listAdapter = UserListAdapter()
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = ActivityMainBinding.inflate(layoutInflater)
            setContentView(binding.root)
    
            binding.recyclerView.adapter = listAdapter
            binding.recyclerView.layoutManager = LinearLayoutManager(applicationContext, RecyclerView.VERTICAL, false)
            listAdapter.submitList(
                listOf(
                    User("あいざわ", "かずき", 29),
                    User("ふじくら", "まさひろ", 52),
                    User("よしずみ", "ひろゆき", 54),
                    User("ほりのうち", "しんいち", 40),
                    User("はすぬま", "よしろう", 37),
                    User("はなわ", "のぶお", 38),
                    User("おじま", "おじま", 31),
                    User("しんざき", "くにひと", 35)
                )
            )
        }
    }
    
    <?xml version="1.0" encoding="utf-8"?>
    <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=".MainActivity">
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="8dp" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    결론



    이전에는 RecyclerView 를 구현한 코드를 기술하면 꽤 힘든 이미지였지만,
    ViewBinding 과 ListAdapter 를 조합해 사용하는 것으로 꽤 스마트한 구현이 되네요.
    Android 공식 라이브러리에서 여기까지 할 수 있게 되어 있는 것은 감동이군요.

    현재의 Android 개발에서는 Epoxy 나 Groupie 등의 라이브러리가 자주 사용된다고 생각합니다만,
    ListAdapter 가 여기까지 간단하게 사용할 수 있게 되면 이 종류의 라이브러리를 사용하는 메리트는 희미해져 네요. 개인적으로는 Epoxy에 시달리는 경우가 많기 때문에 공식 라이브러리만으로 구현할 수 있는 미래가 오기를 바랍니다.

    좋은 웹페이지 즐겨찾기