RecyclerViewとLiveDataを使ってリスト表示

# はじめに
今回は、[前回](https://qiita.com/karass/items/e71698af568f3386b2ab)の続きをやって聞きたいと思います。前回は`RecyclerView`を使ってダミーのデータをリスト表示することができました。今回は`EditText`で入力したデータをリスト表示できるようします。

# 目標
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289190/9fe8f712-fa9d-d35b-5eb6-6c46db2308d2.gif">

# 環境
- Android Studio 3.6.3
- Kotlin 1.3.72

# 作成手順
## Gradle
```build.gradle
dependencies {
    // 省略
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
```

## 実装
### レイアウトの作成
**activity_main.xml**にEditTextとButtonを配置します。

```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">

    <EditText
        android:id="@+id/submit_text"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginTop="45dp"
        android:autofillHints="@string/input"
        android:hint="@string/input"
        android:imeOptions="actionSend"
        android:inputType="text"
        app:layout_constraintBottom_toTopOf="@id/main_recycler_view"
        app:layout_constraintEnd_toStartOf="@+id/button"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/main_recycler_view"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scrollbars="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/submit_text" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="4dp"
        android:text="@string/button"
        app:layout_constraintBottom_toTopOf="@+id/main_recycler_view"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/submit_text" />

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

### MainViewModelの作成
`EditText`から受け取ったデータを`RecyclerView`に反映させるために`LiveData`を使用する。`LiveData`はデータの更新を監視します。今回は`LiveData`を使用しなくても直接`Adapter`に値をセットしてあげれば反映できるが、今後データベースの値を非同期で反映させるためには`LiveData`を使用します。

```MainViewModel.kt
class MainViewModel : ViewModel() {
    val todo = MutableLiveData<String>()

    fun addItem(item: String) {
        todo.value = item
    }
}
```

### MainActivityの定義
ボタンが押された時にEditTextの値を`MainViewModel`の変数`todo`に代入します。その値の変化を監視して`Adapter`に`Item`をセットする。

```MainActivity.kt
class MainActivity : AppCompatActivity() {

    private val mainViewModel = MainViewModel()
    lateinit var adapter: RecyclerAdapter

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

        adapter = RecyclerAdapter()
        main_recycler_view.layoutManager = LinearLayoutManager(this)
        main_recycler_view.adapter = adapter
        main_recycler_view.setHasFixedSize(true)

        add_item_button.setOnClickListener {
            mainViewModel.addItem(submit_text.text.toString())
        }

        mainViewModel.todo.observe(this, Observer {
            adapter.setItem(it)
        })

    }
}
```

### Adapterの定義
`MainActivity`から呼び出された`setItem(item: String)`によって`Adapter`に値をセットします。`notifyDataSetChanged()`を書かないと最新の状態に反映されないので注意。

```RecyclerAdapter.kt
class RecyclerAdapter : RecyclerView.Adapter<RecyclerAdapter.RecyclerViewHolder>(){

    private val todoList = mutableListOf<String>()

    fun setItem(item: String) {
        todoList.add(item)
        notifyDataSetChanged()
    }

    class RecyclerViewHolder(val view: View): RecyclerView.ViewHolder(view) {
        val sampleImg = view.sampleImg
        val sampleTxt = view.sampleTxt
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val item = layoutInflater.inflate(R.layout.recyclerview_item, parent, false)
        return RecyclerViewHolder(item)
    }

    override fun getItemCount(): Int = todoList.size

    override fun onBindViewHolder(holder: RecyclerViewHolder, position: Int) {
        holder.view.let{
            it.sampleImg.setImageResource(R.mipmap.ic_launcher_round)
            it.sampleTxt.text = todoList[position]
        }
    }
}
```

# まとめ
今回は`EditText`の値を`RecyclerView`に反映させましたが、このままだとアプリを終了した時にデータが残らないので[Room](https://developer.android.com/training/data-storage/room?hl=ja)を使ってデータベースに値を保存するなどしなければならない。
次回は`Room`を使ってデータベースに値を保存する方法について書きます！