【Kotlin】RecyclerViewでカスタムリスト-adapter-

1から作ると手間だけどレイアウトはこだわりたい、コピペメモ
練習にどうぞ。
![Screenshot_20241128-105319.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3937966/29851357-8f6c-9f71-0816-fb6c5e626717.png)

## 一連の流れ
1.holderレイアウトを作成
2.data classを定義
3.recyclerviewをActivityレイアウトに置く
4.itemAdapterクラスを定義、List\<data class>を繋ぐ
5.Recyclerview.adapterへ接続

手順は多いがやることは実に簡単。
なんとなくレイアウトとActivityの繋ぎがわかればいい。
## 1.holderレイアウトを作成
item_holder.xmlファイルを作成
文字、画像、チェックボックスなど様々なViewが使えるので自分好みにレイアウトしよう。
リストという繰り返されるレイアウトなのである程度の見やすさは必要。
layoutフォルダ上で右クリック > New > XML > LayoutXMLFile で追加。

![121212.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3937966/c89d8aac-f5c2-301b-2bc1-b34081430ef5.png)

imageはVectorAssetで作成
```xml:baseline_wind_power.xml
<vector android:height="80dp" android:tint="#29A19A"
    android:viewportHeight="25" android:viewportWidth="25"
    android:width="80dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="@android:color/white" android:pathData="M4,3h6v2h-6z"/>
    <path android:fillColor="@android:color/white" android:pathData="M1,7h5v2h-5z"/>
    <path android:fillColor="@android:color/white" android:pathData="M3,19h5v2h-5z"/>
    <path android:fillColor="@android:color/white" android:pathData="M13.73,10.61c0.75,0.23 1.3,0.78 1.57,1.46l4.27,-7.11c0.65,-1.08 0.3,-2.48 -0.78,-3.13c-0.87,-0.52 -1.99,-0.41 -2.73,0.29l-3.43,3.21C12.23,5.7 12,6.23 12,6.78v3.93C12.36,10.56 12.98,10.38 13.73,10.61z"/>
    <path android:fillColor="@android:color/white" android:pathData="M10.61,12.27c0.16,-0.52 0.48,-0.96 0.89,-1.27H3.28C2.02,11 1,12.02 1,13.28c0,1.02 0.67,1.91 1.65,2.19l4.51,1.29c0.53,0.15 1.1,0.08 1.58,-0.21l2.69,-1.61C10.66,14.32 10.3,13.27 10.61,12.27z"/>
    <path android:fillColor="@android:color/white" android:pathData="M22.21,18.61l-2.28,-4.1c-0.27,-0.48 -0.73,-0.83 -1.26,-0.97l-3.18,-0.8c0.03,0.32 0,0.66 -0.1,0.99c-0.32,1.06 -1.28,1.77 -2.39,1.77c-0.61,0 -0.99,-0.22 -1,-0.22V21c-1.1,0 -2,0.9 -2,2h6c0,-1.1 -0.9,-2 -2,-2v-4.28l4.61,4.61c0.89,0.89 2.33,0.89 3.22,0C22.55,20.61 22.71,19.5 22.21,18.61z"/>
    <path android:fillColor="@android:color/white" android:pathData="M12.56,14.43c0.79,0.24 1.63,-0.2 1.87,-1c0.24,-0.79 -0.2,-1.63 -1,-1.87c-0.79,-0.24 -1.63,0.2 -1.87,1C11.32,13.35 11.77,14.19 12.56,14.43z"/>
</vector>
```
枠線が欲しいのでborder.xmlを作成
```xml:border.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_selected="false"
        android:state_pressed="false">
        <shape>
            <stroke
                android:width="1dp"
                android:color="#94A78E">
            </stroke>
        </shape>
    </item>
</selector>
```
全体（色はcolors.xmlで登録）
```xml:item_holder.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">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:background="@drawable/border"
        android:orientation="horizontal"
        tools:ignore="MissingConstraints">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/darkslategray"
                android:layout_weight="1"/>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:orientation="horizontal">

                <ImageView
                    android:id="@+id/imageView"
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:gravity="center"
                    android:padding="5dp"
                    android:src="@drawable/baseline_wind_power_24" />

                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:orientation="vertical">

                    <TextView
                        android:id="@+id/textView_title"
                        android:layout_width="match_parent"
                        android:layout_height="0dp"
                        android:layout_weight="1"
                        android:background="@drawable/border"
                        android:gravity="center"
                        android:text="title"
                        android:textColor="@color/darkslategray"
                        android:textSize="9dp"
                        android:textStyle="bold" />

                    <TextView
                        android:id="@+id/textView_date"
                        android:layout_width="match_parent"
                        android:layout_height="0dp"
                        android:layout_weight="2"
                        android:background="@drawable/border"
                        android:gravity="center"
                        android:text="some"
                        android:textSize="9dp" />
                </LinearLayout>

                <TextView
                    android:id="@+id/textView_text"
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="2"
                    android:background="@drawable/border"
                    android:gravity="center"
                    android:text="text"
                    android:textSize="15dp" />

                <CheckBox
                    android:id="@+id/checkBox"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:layout_weight="0.5" />
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

```
レイアウトは分かりやすいこちらを参考（有難うございます）

https://qiita.com/mii-chang/items/ee965c1e8826d4e59414

## 2.data classを定義
画像の変更、チェックボックスの取得はコツがいるので後日。
```kotlin:MainActivity
data class ItemData(
    val title:String,
    val some:Date,
    val text:String,
)
```
## 3.recyclerviewをActivityレイアウトに置く
```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"
    android:background="@color/darkslategray"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="15dp"
        android:background="@color/white"
        android:orientation="vertical">

        <TextView
            android:id="@+id/text_v"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:text="Hello World!" />
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="7"
            tools:ignore="MissingConstraints">

        </androidx.recyclerview.widget.RecyclerView>
    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
```
## 4.itemAdapterクラスを定義、List\<data class>を繋ぐ
短い記述だが各種設定が詰まっている。
```kotlin:Adapter.kt
package com.example.myapplication
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import java.time.format.DateTimeFormatter
import java.util.Locale

//引数に注目しよう
class Adapter (private var rowDataList:MutableList<ItemData>):RecyclerView.Adapter<Adapter.ItemViewHolder>(){
    //日付の描画フォーマット
    private var format = DateTimeFormatter.ofPattern("MMMM",Locale.US)
    //ここでitem_holder.xmlレイアウト（holder）がactivity_main.xmlのRecyclerViewに適合する（adapter）
    class ItemViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
        val title: TextView = itemView.findViewById(R.id.textView_title)
        val date: TextView = itemView.findViewById(R.id.textView_date)
        val text: TextView = itemView.findViewById(R.id.textView_text)
    }

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

    override fun getItemCount(): Int = rowDataList.size

    //List内容を描画する
    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        val rowData = rowDataList[position]
        println(rowData)
        holder.apply {
            rowData.also {
                title.text = it.title
                date.text =  format.format( it.some)
                text.text = it.text
            }
        }
    }
}

```
## 5.Recyclerview.adapterへ接続
忘れがちなRecyclerView.LayoutManager
```kotlin:MainActivity
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.myapplication.databinding.ActivityMainBinding
import java.time.LocalDate
import java.util.Date

class MainActivity : AppCompatActivity() {

    private val local = LocalDate.now()
    private var dataSet = mutableListOf<ItemData>()
    private var itemAdapter:Adapter? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        //RecyclerViewで表示するアイテムの数を返す
        binding.recyclerView.setHasFixedSize(true)
        //recyclerview アダプタの接続準備
        val layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(this)
        binding.recyclerView.layoutManager = layoutManager
    }
    private fun dateAdd(st:String,text:String):MutableList<ItemData>{
        val item = ItemData(
            st,
            local.plusMonths( dataSet.size.toLong()),
            text
        )
        dataSet.run {if(this.none { it == item })this.add(0,item)}
        return dataSet
    }

    override fun onStart() {
        super.onStart()
        dateAdd("${dataSet.size}番目","AAAAAA")
        dateAdd("${dataSet.size}番目","BBBBBB")
        dateAdd("${dataSet.size}番目","CCCCCC")
        //上記に入れたListをitemAdapterレイアウト設定でrecyclerviewアダプタに接続する
        itemAdapter = Adapter(dataSet)
        binding.recyclerView.adapter = itemAdapter
    }
}

```
## おまけ
実機があるなら一度アプリを閉じてみよう。
![Screenshot_20241128-105331.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3937966/f26c3833-f155-8b9c-5539-614e5708a653.png)
##### 増えている‼（onStart()で再び呼び出されたため）

ここでAdapterのprintln(rowData)でログを確認する。
```:log
//最初
ItemData(title=2番目, some=2025-01-28, text=CCCCCC)
ItemData(title=1番目, some=2024-12-28, text=BBBBBB)
ItemData(title=0番目, some=2024-11-28, text=AAAAAA)
//閉じたあと
ItemData(title=5番目, some=2025-04-28, text=CCCCCC)
ItemData(title=4番目, some=2025-03-28, text=BBBBBB)
ItemData(title=3番目, some=2025-02-28, text=AAAAAA)
ItemData(title=2番目, some=2025-01-28, text=CCCCCC)
ItemData(title=1番目, some=2024-12-28, text=BBBBBB)
ItemData(title=0番目, some=2024-11-28, text=AAAAAA)
```
それぞれ一度だけ呼ばれている。
下記を入れれば任意のタイミングでリスト更新ができるはずだ。
```kotlin:MainActivity
itemAdapter = Adapter(dataSet)//dataSetの変更があれば
binding.recyclerView.adapter = itemAdapter//反映させる
```