AndroidのRecyclerViewをJetpackComposeで実装する時の違い

## はじめに

こんにちは。
株式会社アイスタイルで@cosmeアプリのAndroidエンジニアをしている山本と申します。

今回は、Android開発でよく用いられるRecyclerViewをComposeで実装するとどんな違いがあるのかについて解説いたします。
RecyclerViewは、便利なリストビューを実装するためのクラスです。 Jetpack
Composeで同じリストビューを実装する場合、どのような違いがあるのかを詳しく比較してみましょう。
(この情報は、2024/12時点での情報となります。)

## RecyclerViewとは

RecyclerViewは、AndroidのListViewをより柔軟に実装するためのクラスです。
RecyclerViewは、ListViewの進化版とも言えるクラスで、ListViewの機能に加えて、リストの要素を再利用することでパフォーマンスを向上させます。
ただし、RecyclerViewはXMLでレイアウトを記述し、AdapterクラスやViewHolderクラスを実装する必要があるためコードが複雑になりがちです。

## Jetpack Composeとは

Jetpack Composeは、Googleが提供するAndroidのUIツールキットで、Kotlin言語でUIを記述できます。
XMLでUIを記述する代わりに、KotlinのコードでUIを記述できるため、コードがシンプルになり、開発効率が向上します。
Jetpack Composeでは、RecyclerViewの代わりに、LazyColumnやLazyRowなどのコンポーズ関数を用いてリストビューを実装できます。

## RecyclerViewをJetpack Composeで実装する時の違い

### レイアウトの記述方法

RecyclerViewでは、XMLでレイアウトを記述しますが、Jetpack Composeでは、KotlinのコードでUIを記述します。
例えば、RecyclerViewでは、以下のようにXMLでレイアウトを記述します。

```xml
<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
```

Composeでは、以下のようにKotlinのコードでUIを記述します。

```kotlin
LazyColumn {
    // リストの要素を記述
    }
```

### 必要になるクラスの違い

RecyclerViewを使う場合、AdapterクラスやViewHolderクラスを実装する必要がありますが
Jetpack Composeでは、これらのクラスを使う必要がありません。
代わりに、LazyColumnやLazyRowなどのコンポーズ関数を使ってリストビューを実装します。

#### RecyclerViewを使う場合の必要なクラス

- RecyclerView.Adapterを継承したクラス

```kotlin
class MyAdapter(private val itemList: List<MyItem>) :
    RecyclerView.Adapter<MyAdapter.MyViewHolder>() {

    class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val textView: TextView = itemView.findViewById(R.id.textView)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
        return MyViewHolder(view)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.textView.text = itemList[position].text
    }

    override fun getItemCount(): Int {
        return itemList.size
    }
}
```

- RecyclerView.ViewHolderを継承したクラス

```kotlin
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val textView: TextView = itemView.findViewById(R.id.textView)
}
```

- 要素１つ分のlayoutを記述したXMLファイル(R.layout.item_layout)

```xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView android:id="@+id/textView" android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:text="Item" />
</LinearLayout>
```

- RecyclerViewを制御するActivityやFragment

```kotlin
class MyFragment : Fragment() {

    private lateinit var recyclerView: RecyclerView

    data class MyItem(
        val text: String
    )

    val list = listOf(
        MyItem("Item 1"),
        MyItem("Item 2"),
        MyItem("Item 3")
    )

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = MyAdapter(list)
        return view
    }
}
```

#### Jetpack Composeを使う場合の必要なクラス

- LazyColumnやLazyRowなどのコンポーズ関数を使ってリストビューを実装するComposable関数を記述するクラス

```kotlin
@Composable
fun MyList(list: List<MyItem>) {
    LazyColumn {
        items(items = list) { item ->
            Text(text = item.text)
        }
    }
}
```

- Composable関数を呼び出すActivityやFragment

```kotlin
class Fragment : MyFragment() {

    data class MyItem(
        val text: String
    )

    val list = listOf(
        MyItem("Item 1"),
        MyItem("Item 2"),
        MyItem("Item 3")
    )

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        setContent {
            MyList(list)
        }
        return view
    }
}
```

このように、Jetpack Composeを使うと、RecyclerViewを使う場合に比べて必要なクラスやコード量が少なく、シンプルにリストビューを実装できます。

### リストの要素の再利用の違い

RecyclerViewでは、リスト内の要素が更新された時に、Adapter.notifyDataSetChanged()
などのメソッドを呼び出して、能動的にリストのデータ更新を行い再描画を定義する必要があります。
一方、Jetpack Composeでは、リストの要素を再利用するための仕組みが内部で自動的に行われるため、リスト内の要素が更新された時に、再描画が自動的に行われます。
この機能は再コンポジションという機能によって実現されており、リスト内の要素が更新された時に、必要な部分だけを再描画できます。
それによって再描画するためパフォーマンスが向上します。
RecyclerViewでは、データの更新通知の実装が必要ですが、Jetpack Composeでは必要ありません。

## まとめ

以上、AndroidのRecyclerViewをJetpack Composeで実装する時の違いについて解説いたしました。
Jetpack Composeを使うと、RecyclerViewを使う場合に比べて、レイアウトの記述方法や必要なクラス、リストの要素の再利用などに違いがあります。
Jetpack Composeの方が、シンプルでコード量が少なく、再利用時のパフォーマンスが向上するため、より効率的にリストビューを実装できます。
他にも、従来のXMLでのUI記述に比べて、KotlinでUIを記述することで、制御やデータの受け渡しがしやすくなり、開発効率を向上させられます。
私もJetpack Composeを使っての開発はまだ始めたばかりですが、こんなに簡単にリストビューを実装できるなんてと感動しました。
ぜひ、Jetpack Composeを使ってみて、Android開発の効率化に役立ててみてください。
少しでも参考になれば幸いです。

## 参考

- [RecyclerView | Android Developers](https://developer.android.com/reference/kotlin/androidx/recyclerview/widget/RecyclerView)
- [Jetpack Compose | Android Developers](https://developer.android.com/jetpack/compose)