RecyclerViewでリスト表示を実装する

# やりたいこと
RecyclerViewを使用して、あるデータをリスト表示するシンプルなアプリです。

<img width="300" alt="Screenshot_1614933541.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663839/8ddfff40-df00-a778-01a9-2f98a74a9d60.png">

# 環境
**Android Studio: 4.1.1**
**Kotlin: 1.4.10**

# 実装
# データクラスを作成する 
表示する情報のデータクラスを作成します。
classの前に`data`をつけることでデータクラスとして作成されます。
今回はFruitという名前にしています。

```kotlin:Fruit.kt
data class Fruit(
    var name: String,
    var imageId: Int
)
```

# アイテムのレイアウトファイルを作成する
次に、RecyclerView内にリスト表示される1つ1つのアイテムのレイアウトファイルを作成します。

```list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <ImageView
        android:id="@+id/fruit_image"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_marginTop="18dp"
        android:layout_marginEnd="18dp"
        android:layout_marginStart="18dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/fruit_name"
        tools:src="@drawable/apple" />

    <TextView
        android:id="@+id/fruit_name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="4dp"
        android:layout_marginEnd="4dp"
        android:textSize="24sp"
        app:layout_constraintStart_toEndOf="@id/fruit_image"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="@id/fruit_image"
        app:layout_constraintBottom_toBottomOf="@id/fruit_image"
        tools:text="apple" />

</androidx.constraintlayout.widget.ConstraintLayout>
```

# レイアウトにRecyclerViewを設置する

このアプリでは、一部データバインディングを使用するので、アプリモジュールの`build.gradle`に`dataBinding`を追加します。

**build.gradle(:app)**

```
android {
        ...
        dataBinding {
            enabled = true
        }
    }
```

RecyclerViewにはリストの表示方法を設定する`layoutManager`が必須なので、忘れないように注意が必要です。

```xml:fragment_fruit.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".FruitFragment">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/fruit_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
```

# Adapterを作成する
## 1. Adapterクラスの作成

```kotlin:FruitAdapter.kt
class FruitAdapter: RecyclerView.Adapter<FruitAdapter.ViewHolder>() {
}
```
`class FruitAdapter`にカーソルを合わせ、`option+Enter(Macの場合) -> Implement members`を選択することで、Adapterクラスに必要な3つのメソッドを自動で生成してくれます。

```kotlin:FruitAdapter.kt
class FruitAdapter: RecyclerView.Adapter<FruitAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitAdapter.ViewHolder {
        TODO("Not yet implemented")
    }

    override fun onBindViewHolder(holder: FruitAdapter.ViewHolder, position: Int) {
        TODO("Not yet implemented")
    }

    override fun getItemCount(): Int {
        TODO("Not yet implemented")
    }
}
```

**onCreateViewHolder**：新しい`ViewHolder`を作成する必要があるたびに呼ばれる。Viewを生成して、`ViewHolder`を返す。
**onBindViewHolder**：`position`から適切なデータを取得して、各アイテムのレイアウトと関連付ける。
**getItemCount**：表示するアイテム数を設定する。

## 2. ViewHolderを作成

まだViewHolderを作成していないため、`<FruitAdapter.ViewHolder>`の部分でエラーが出るので、FruitAdapterクラス内にViewHolderを作成します。

```kotlin:FruitAdapter.kt
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val fruitName: TextView = itemView.findViewById(R.id.fruit_name)
        val fruitImage: ImageView = itemView.findViewById(R.id.fruit_image)
}
```

## 3. Adapterクラス内の実装

- **データを保持する変数を作成**

```kotlin:FruitAdapter.kt
var data = ArrayList<Fruit>()
```

- **getItemCount()**

この値がRecyclerView内に表示されるアイテムの数になります。

```kotlin:FruitAdapter.kt
override fun getItemCount() = data.size
```

- **onBindViewHolder()**

`onBindViewHolder()`関数では、`position`から取得した適切なデータを、textViewなどに設定します。

```kotlin:FruitAdapter.kt
override fun onBindViewHolder(holder: FruitAdapter.ViewHolder, position: Int) {
        val item = data[position]

        holder.fruitName.text = item.name
        holder.fruitImage.setImageResource(item.imageId)
    }
```

- **onCreateViewHolder()**

layoutInflaterでViewを作成して、ViewHolderを返します。

```kotlin:FruitAdapter.kt
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitAdapter.ViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val view = layoutInflater.inflate(R.layout.list_item, parent, false)
        return ViewHolder(view)
    }
```

# コード
最終的にこのようになれば、Adapterクラスの完成です。

```kotlin:FruitAdapter.kt
class FruitAdapter: RecyclerView.Adapter<FruitAdapter.ViewHolder>() {

    var data = ArrayList<Fruit>()

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

    override fun onBindViewHolder(holder: FruitAdapter.ViewHolder, position: Int) {
        val item = data[position]

        holder.fruitName.text = item.name
        holder.fruitImage.setImageResource(item.imageId)
    }

    override fun getItemCount() = data.size

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val fruitName: TextView = itemView.findViewById(R.id.fruit_name)
        val fruitImage: ImageView = itemView.findViewById(R.id.fruit_image)
    }
}
```

# RecyclerViewにAdapterを設定する
Adapterの設定は全て`onCreateView()`メソッド内で行います。
（アクティビティの場合は、`onCreate()`メソッド）

## 1. Adapterを初期化する
```kotlin:FruitFragment
val adapter = FruitAdapter()
```

## 2. データを格納する
今回はサンプルなので、あらかじめ用意したデータを表示させるようにしています。
基本的にはデータベース等から取得した動的なデータを使ったりすると思います。

```kotlin:FruitFragment
val adapter = FruitAdapter()

val fruitData = arrayListOf(
            Fruit("Apple", R.drawable.apple),
            Fruit("Banana", R.drawable.banana),
            Fruit("Cherry", R.drawable.cherry),
            Fruit("Grape", R.drawable.grape),
            Fruit("Orange", R.drawable.orange),
            Fruit("Peach", R.drawable.peach),
            Fruit("Pineapple", R.drawable.pineapple),
            Fruit("Strawberry", R.drawable.strawberry)
        )

adapter.data = fruitData
```

## 3. RecyclerViewにアダプターをセットする
```kotlin:FruitFragment
binding.itemList.adapter = adapter
```

# コード
```kotlin:FruitFragment
class FruitFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        val binding: FragmentFruitBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_fruit, container, false)

        val adapter = FruitAdapter()

        val fruitData = arrayListOf(
            Fruit("Apple", R.drawable.apple),
            Fruit("Banana", R.drawable.banana),
            Fruit("Cherry", R.drawable.cherry),
            Fruit("Grape", R.drawable.grape),
            Fruit("Orange", R.drawable.orange),
            Fruit("Peach", R.drawable.peach),
            Fruit("Pineapple", R.drawable.pineapple),
            Fruit("Strawberry", R.drawable.strawberry)
        )

        adapter.data = fruitData

        binding.itemList.adapter = adapter

        return binding.root
    }
}
```

今回はRecyclerViewの使い方がメインのため、データバインディングの説明は省いています。