【Android】RecyclerViewの超基本

# はじめに
よくリスト表示で使われるRecyclerView。
Android始めたての時はなかなか基本的な使い方がわからず困った記憶があるので、改めて初心にかえって基本的な実装方法を記してみたいと思います。
間違いや気になる点がありましたらお気軽にコメント頂けますと幸いです。

# 完成形
こんな感じのシンプルなリストをRecyclerViewで作っていきたいと思います。
![Screenshot_1653211534.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1274260/06d7ea82-d86d-663e-1d15-2615565ba1a1.png)




# 実行環境

|  項目      |  情報                |      
| ------ | ---------------------------- | 
| PC     | MacBook Pro (14-inch,2021) |     
| CPU    | Apple M1 Pro 10-core                |      
| GPU  | Apple M1 Pro 16-core                       |      
| OS     | macOS Monterey (12.0.1)<br>  |   
|  Android Studio| Arctic Fox 2020.3.1 Patch4 | 

## 前提
* 基本的なところにフォーカスしたいので、値を外部から入れたりdata classを作るなどの処理は行いません。
* 親FragmentにRecyclerViewを載せる構成で作成していきます。
* 本記事ではビューの取得にビューバインディングを使用しています。ビューバインディングってなんぞやという方は[こちら](https://developer.android.com/topic/libraries/view-binding?hl=ja)

## RecyclerViewの概要
RecyclerViewは
* RecyclerView自体を表示する親View
* リスト1行分を表示するためのレイアウト&ViewHolder
* 表示処理を管理するAdapter

で構成されています。
それぞれについてはこれからコードを追いながら見ていきます。

## RecyclerViewを載せる親Fragmentの実装
まずは親FragmentにRecyclerViewを追加していきます。

```MainFragment.kt
class MainFragment : Fragment(R.layout.main) {

    var binding: MainBinding? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding = MainBinding.bind(view)
    }
}
```

```main.xml
<?xml version=“1.0” encoding=“utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android=“http://schemas.android.com/apk/res/android”
  android:layout_width=“match_parent”
  android:layout_height=“match_parent”
  xmlns:app=“http://schemas.android.com/apk/res-auto”>

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

</androidx.appcompat.widget.LinearLayoutCompat>
```

MainFragment.ktに関してはコンストラクタにID指定していたりビューバインディングを使用したりしていますが、細かいところなのでこの辺りのやり方はご自由にどうぞ。とりあえず現段階で表示ができれば問題ありません。

今回は全面にrecyclerViewを表示したいので、widthもheightもmatch_parentにしています。

それ以外は特別なことなしていませんが、1点重要なのが`app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"`
の部分です。これがないと表示されません。
今回指定しているLinearLayoutManagerは、リスト形式で要素を配置します。TwitterのタイムラインやTODOリストみたいなイメージですね。
他には写真フォルダのように要素を配置するGridLayoutManagerや、GridLayoutの高さや幅をバラバラにできるStaggeredGridLayoutManagerが用意されています。[詳細はこちら](https://developer.android.com/guide/topics/ui/layout/recyclerview?hl=ja#plan-your-layout)


## ViewHolderの作成
ViewHolderはリスト1行分の箱のような役割を担います。
RecyclerViewはViewHolderを使い回すことで成り立っています。
```main_item.xml
<!— 1行分のレイアウトを作成 —>
<TextView
  xmlns:android=“http://schemas.android.com/apk/res/android”
  android:id=“@+id/main_item_text”
  android:layout_width=“match_parent”
  android:layout_height=“100dp”
  android:text=“aaa”
  android:background=“@color/teal_200" />
```
まずはリスト1個分のレイアウトを作成します。
今回はごくシンプルにTextViewのみを配置しています。
ここを凝ったレイアウトにすれば、凝ったレイアウトが続々と表示されるようなリストにできます。


次に、ViewHolderの作成です。
```MainViewHolder.kt
class MainViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
    val textView: TextView = itemView.findViewById(R.id.main_item_text)
}
```
ここの引数の入れ方は色々やり方があるようで、
```MainViewHolder.kt
class MainViewHolder(val binding: MainItemBinding): RecyclerView.ViewHolder(binding.root)
```
とかやっているのも目にします。今回は前者で実装します。
ちなみにこの引数には後程ご紹介するAdapterで値を入れます。


## Adapterの実装
いよいよrecyclerViewのAdapterを作成していきます。

```MainAdapter.kt
class MainAdapter : RecyclerView.Adapter<MainViewHolder>() {

// その名の通りViewHolderを作成。MainViewHolderの引数にinflateしたレイアウトを入れている
  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
    return MainViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.part_item_model, parent, false)
    )
  }

// ViewHolder内に表示するデータを指定。
  override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
    holder.binding.MainItemText.text = "${position}番目です"
  }

// 表示したいリストの数を指定
  override fun getItemCount(): Int {
    return 10
  }
}
```

上から順に、
* クラス宣言部分
* onCreateViewHolderメソッド
* onBindViewHolderメソッド
* getItemCountメソッド

の順番で見ていきます。
### クラス宣言部分
```MainAdapter.kt
class MainAdapter : RecyclerView.Adapter<MainViewHolder>() {
```
親クラスとしてRecyclerView.Adapterを指定します。
その次の<>内には今回使用するViewHolder（今回ならMainViewHolder）を指定しますが、＜RecyclerView.ViewHolder＞としておくこともできるようです。

Android StudioでRecyclerView.Adapterを継承した状態でクラス名にカーソルを合わせると下画像のようなエラーが出ると思いますが、それは必要なメソッドを実装していないためです。
![スクリーンショット 2022-05-10 23.26.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1274260/65cb94c9-8b61-ee85-ee42-09be0d67c129.png)
これは下記3つのメソッドを実装するとエラーが消えます。（画像内の「implement membersをクリックすると説明文ともにメソッドの雛形が用意されます。）

### onCreateViewHolderメソッド
```MainAdapter.kt
// その名の通りViewHolderを作成。
  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
    return MainViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.part_item_model, parent, false)
    )
  }
```
ここでは先ほど作成したViewHolderをインスタンス化しています。その際に、先ほど作成したリスト1個分のレイアウト（＝main_item.xml）をインフレートして引数に渡していますね。
つまりここではリスト1個分のレイアウトを作っている的な理解でいいのではないかと思います。


### onBindViewHolderメソッド
リスト表示をする時、中身のデータが全て同じということはほとんどないと思います。
そのため、表示するデータをこのonBindViewHolderメソッド内で設定していきます。

```MainAdapter.kt
// ViewHolder内に表示するデータを指定。
  override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
    holder.binding.MainItemText.text = "${position}番目です"
  }
```

先ほど作成したTextViewでは`text = "aaa"`となっていましたが、ここで新たにデータをセットしているので実際には`○番目です`が表示されていますね。
実際はlist型でデータを渡し、[position]番目のデータを表示するといった形で使うことが多いです。

### getItemCountメソッド
```MainAdapter.kt
// 表示したいリストの数を指定
  override fun getItemCount(): Int {
    return 10
  }
```
表示したいリストの数を返します。（このくらいの記述量であれば=で書いた方がいいかもしれませんね）
`return 100`とすれば`100番目です`が表示されるまでリストが続きますし、`return 1000`とすれば当然`1000番目です`が表示されるまでリストが続きます（！）

# Adapterを紐付ける
ここまで来たらあとはAdapterをFragmentと紐づけるだけです！
冒頭で作成したMainFragmentに下記のように付け足してください。

```MainFragment.kt
class MainFragment: Fragment(R.layout.main) {
  var binding: MainBinding? = null

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

     binding = MainBinding.bind(view)
+    binding?.MainRecyclerView?.adapter = MainAdapter()
  }
```
冒頭で作成した`main.xml`内にあるrecyclerViewにMainAdapterを指定します。意外にこの紐付け処理は忘れがちなので要注意です⚠️

## さいごに
RecyclerViewは最初はなかなか理解しにくいと思うので、必要最低限なところのみ解説しました。
まだまだいろんな機能や実装方法があるので、基礎を固めたらぜひ深掘りしてみていただきたいです！