RecyclerViewでデータを複数件表示する方法

# 対象者
この記事はこんな人におすすめです！

- RecyclerView使ってみたいけどRecyclerViewの使い方がわからない
- データを複数件表示したい

# RecyclerViewとは
RecyclerViewの概要については
[Create a List with RecyclerView](https://developer.android.com/guide/topics/ui/layout/recyclerview?hl=ja)
に書いてあるのですが、重要なものは3つです。

| 名前 | 説明 |
|:----------:|:---|
| Adapter    | RecyclerViewに表示するデータの保持、ViewHolderの生成、データをViewに設定 |
| ViewHolder | 各行のViewに対する参照を保持する |
| LayoutManager | RecyclerViewにViewをどのように配置するか設定を行う |

どのようにこの3つが使われているのかサンプルアプリを作りながら解説していきます。

# RecyclerViewを使ったサンプルアプリ
今回は、RecyclerViewを使ってメモを複数件表示するアプリを作っていきたいと思います。
表示するメモの要素は以下の４つです。

- ユーザのアイコン：ImageView
- ユーザ名：TextView
- 日付：TextView
- メモの内容：TextView

<img alt="Screenshot_1560243129.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434603/97e8455f-f10f-d5c0-0c4d-034942f154b2.png" width=40% />

# 環境
- Android Studio 3.4
- Kotlin 1.3.31-release-Studio3.4-1
- AndroidX

# 実装方法
## RecyclerViewを使うための準備
```gradle:build.gradle(app)
dependencies {
    //この一文を追加
    implementation 'androidx.recyclerview:recyclerview:1.0.0'
}
```
## Layout
メモの内容を表示する`list_memo.xml` を作成していきます。

```xml:list_memo.xml
<?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:paddingBottom="16dp">

    <ImageView
        android:id="@+id/userIcon"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:srcCompat="@tools:sample/avatars" />

    <TextView
        android:id="@+id/userName"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:text="fu_neko"
        app:layout_constraintStart_toEndOf="@+id/userIcon"
        app:layout_constraintTop_toTopOf="@+id/userIcon" />

    <TextView
        android:id="@+id/date"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="3dp"
        android:text="2019/06/10"
        android:textSize="12sp"
        app:layout_constraintStart_toEndOf="@+id/userName"
        app:layout_constraintTop_toTopOf="@+id/userName" />

    <TextView
        android:id="@+id/content"
        android:layout_width="300dp"
        android:layout_height="0dp"
        android:layout_below="@+id/userName"
        android:layout_marginStart="8dp"
        android:layout_marginTop="4dp"
        android:text="今日のご飯は、カレーライス、とんかつ、寿司、チャーハン、カレーライス！"
        android:textStyle="bold"
        app:layout_constraintStart_toEndOf="@+id/userIcon"
        app:layout_constraintTop_toBottomOf="@+id/userName" />
</androidx.constraintlayout.widget.ConstraintLayout>
```

下記のようなDesign画面が表示されます。

![スクリーンショット 2019-06-11 10.56.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434603/33bf1deb-a3df-0995-df88-27c709796b44.png)


## Data Class
メモの内容を保持するデータクラスを作成します。
`Memoクラス`は、第一引数にメモの内容、第二引数にメモをした日付を持つデータクラスです。

```kotlin:Memo.kt
package com.example.recyclerviewapp.model

data class Memo(val content: String, val date: String)
```

## Adapter, ViewHolder
ViewHolderの生成やデータとViewの設定を行う`MemoViewAdapterクラス`を作成します。

```kotlin:MemoViewAdapter.kt
package com.example.recyclerviewapp.adapter

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.recyclerviewapp.R
import com.example.recyclerviewapp.model.Memo

class MemoViewAdapter(private val context: Context, private val memoList: List<Memo>) :
    RecyclerView.Adapter<MemoViewAdapter.MemoViewHolder>() {
    
    class MemoViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val userIconImageView: ImageView = view.findViewById(R.id.userIcon)
        val userNameTextView: TextView = view.findViewById(R.id.userName)
        val dateTextView: TextView = view.findViewById(R.id.date)
        val contentTextView: TextView = view.findViewById(R.id.content)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MemoViewHolder =
        MemoViewHolder(LayoutInflater.from(context).inflate(R.layout.list_memo, parent, false))

    override fun getItemCount(): Int = memoList.size

    override fun onBindViewHolder(holder: MemoViewHolder, position: Int) {
        holder.userIconImageView.setImageResource(R.mipmap.ic_launcher)
        holder.userNameTextView.text = "fu_neko"
        holder.dateTextView.text = memoList[position].date
        holder.contentTextView.text = memoList[position].content
    }
}
```

### #onCreateViewHolder
`onCreateViewHolder`では、`list_memo.xml`を読み込み、Viewを生成しています。
その後、生成したViewをViewHolderに渡している。

### #MemoViewHolder
`OnCreateViewHolder`で生成されたViewに対して、`findViewById`でViewの紐付けを行っている。

### #onBindViewHolder
`onBindViewHolder`では、ViewHolderの持つViewにデータを設定している。


| データ | 設定 |
|:--------------------------:|:----------------------:|
| userIconImageView          | ドロイド君のアイコン      |
| userNameTextView           | fu_neko                |
| dateTextView               | Memoクラスのdate変数     |
| contentTextView            | Memoクラスのcontent変数  |

### #getItemCount
`getItemCount`では、表示するメモの総数を取得しています。

## Activity
### RecyclerViewのWidgetを設置
RecyclerViewのWidgetを`activity_main.xml`に追加していきます。

```xml:activity_main.xml
<?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/memoRecyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
    </androidx.recyclerview.widget.RecyclerView>
</androidx.constraintlayout.widget.ConstraintLayout>
```

下記のようなDesign画面が表示されます。

![スクリーンショット 2019-06-11 17.02.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434603/26899868-5fa0-d005-15be-1558cf609eca.png)

### RecyclerViewにAdapterとLayoutManagerを設定

`MainActivity.kt`では、まずAdapterに渡すデータの生成を行っています。
次にRecyclerViewを取得し、`Adapter`と`LayoutManager`の設定を行います。
今回は、縦にViewを並べたいので`LinearLayoutManager`を使っています。


```kotlin:MainActivity.kt
package com.example.recyclerviewapp

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.recyclerviewapp.adapter.MemoViewAdapter
import com.example.recyclerviewapp.model.Memo
import java.text.SimpleDateFormat
import java.util.Date

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //テストデータの生成
        val date = SimpleDateFormat("yyyy/MM/dd").format(Date())
        val memoList = mutableListOf<Memo>()
        repeat((0..100).count()) { memoList.add(Memo("僕の名前は麻婆", date)) }

        //RecyclerViewにAdapterとLayoutManagerを設定
        findViewById<RecyclerView>(R.id.memoRecyclerView).also { recyclerView: RecyclerView ->
            recyclerView.adapter = MemoViewAdapter(this, memoList)
            recyclerView.layoutManager = LinearLayoutManager(this)
        }
    }
}
```

# まとめ
今回は、アプリ開発をするにあたってRecyclerViewを使う機会があり、その際に学んだことをサンプルアプリを作りながら説明していきました。もしここ間違っているよ等あればコメントください。