Android MVVM  + LiveData + DataBinding(初心者向け)

## 初めに
社内でAndroidの勉強会を開催する機会があり、記事を書こうと思いました。
今回は「AndroidArchitectureComponents」を使ってMVVMを実現する(実装する)内容となっています。別記事で、[MVVMの説明した記事](https://qiita.com/zakimiiiii/items/70b586b8a108783a0426)を書いてます。
※内容がおろそかだったらごめんなさい:relaxed:

## 今回作るもの

![Studyアプリ.gif](https://qiita-image-store.s3.amazonaws.com/0/177058/c85353e2-9d8f-80a9-200b-112018ea64cb.gif)

以下を行う双方向データバインディングアプリ

- 画面を開いた時に、Modelからデータを取得しViewに変更通知
- 画面から文字を入力し、Viewに変更を通知

## 設計イメージ
<img width="500" alt="スクリーンショット 2019-04-10 13.38.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/177058/794c03f7-249a-144b-b84e-d262a324a4ac.png">

## 実装手順
Model実装 → ViewModel実装 → View実装

## さぁ実装！の前に
実装の前に、知っていただきたいことがあります。
今回使用するDataBinding、LiveData、ViewModelをざっくり知っていただこうと思います。
それを知らないと何やっているかわからないと思うからです。ちなみに、別記事で詳しく述べる予定です。

## DataBindingとは
Viewとデータ情報を静的または動的に結合する技術
####単方向その1
ViewModelの値を変更する -> Viewに自動で反映される
####単方向その2
ユーザがViewに入力 -> 自動でViewModelに値がセットされる
####双方向
ViewModelの値を変更する -> Viewに自動で反映される
ユーザがViewに入力 -> 自動でViewModelに値がセットされる


## LiveDataとは
値の変更をObserveできるデータホルダー
#### LiveData
外部から変更不可なLiveData
#### MutableLiveData ← 今回はこれだけ知っていればいい!
外部から変更可能なLiveData
#### MediatorLiveData
複数のLiveDataを束ねて管理するMutableLiveData

## ViewModelとは
Activityの画⾯回転時のデータ保持
Activityの複数Fragment間でのデータ受け渡し
LiveDataと併⽤することが多い　← 今回はこれだけ知っていればいい!
プロセス停⽌後は復旧できない
データの永続化ではない
ViewModelProviders.of()とViewModelProvider.get()を使い、⾃分でnewしない！　←　あ、これも知ってて！


## 実装
### Gradleの設定
```build.gradle
apply plugin: 'kotlin-kapt'

android {
    dataBinding {
        enabled = true
    }
}

dependencies {
    def archComponents_version = '2.0.0-beta01'
    implementation "androidx.lifecycle:lifecycle-extensions:$archComponents_version"
    kapt "androidx.lifecycle:lifecycle-compiler:$archComponents_version"
}
```

### Model(Repository)
シンプルな初期値のみを取得する

```MainRepository.kt
class MainRepository {
    fun fetchText(): String {
        return "初期値！！"
    }
}
```

### ViewModel
変更可能なLiveDataにModelから取得した情報を格納する


```MainViewModel.kt
class MainViewModel : ViewModel() {

    var liveDataText: MutableLiveData<String> = MutableLiveData()

    fun fetchText() {
        liveDataText.value = MainRepository().fetchText()
    }
}
```

### レイアウト
xmlファイルのルートを<layout>にして、使用するオブジェクトを定義する<data>と、Viewを実装
Viewからバインドしたdataにアクセスするには、`@{}`でくくる
また、`@{}`はnullを許容するようになっており、NullPointerExceptionは発生しない

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

    <data>
        <variable name="view_model" type="com.example.mvvmstudy.viewmodel.MainViewModel"/>
        <import type="java.lang.Integer" />
        <import type="android.view.View"/>
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="20dp">

        <EditText
                android:id="@+id/edit_text"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="50dp"
                android:text="@={view_model.liveDataText}"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>

        <TextView
                android:id="@+id/count_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:text="@{`文字数：` + Integer.toString(view_model.liveDataText.length())}"
                app:layout_constraintTop_toBottomOf="@+id/edit_text"
                app:layout_constraintStart_toStartOf="parent"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>
```

### View(ActivityとFragment)
ActivityはFragmentを呼ぶだけ

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

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

        if (savedInstanceState == null) {
            supportFragmentManager
                .beginTransaction()
                .add(R.id.container, MainFragment())
                .commit()
        }
    }
}
```

- `<layout>`でくくったレイアウトファイルがあると、自動的にxmlファイル名に応じたBindingクラスが作られます。
- `DataBindingUtil.inflate`でBindingしたViewを取得する。
- `binding.viewModel = viewModel`でレイアウトファイルに定義したViewModelにViewModelのインスタンスを格納。
- ViewModelのLiveDataをオブザーブし、データに変更があったらテキストを変更するようにする。


```MainFragment.kt
class MainFragment : Fragment() {
    private lateinit var binding: FragmentMainBindingBinding
    private val viewModel: MainViewModel by lazy {
        ViewModelProviders.of(this).get(MainViewModel::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        viewModel.liveDataText.observe(this, Observer {
            binding.countText.text = "文字数：" + it.length.toString()
        })
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_main_binding, container, false)
        binding.viewModel = viewModel
        viewModel.fetchText()
        return binding.root
    }
}

```

しかし、これでは単方向データバインディング。

- 双方向データバインディングするために、`binding.lifecycleOwner = viewLifecycleOwner`を設定する。
- オブザーブしていた処理は不要になったため、削除

```MainFragment.kt
class MainFragment : Fragment() {
    private lateinit var binding: FragmentMainBindingBinding
    private val viewModel: MainViewModel by lazy {
        ViewModelProviders.of(this).get(MainViewModel::class.java)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_main_binding, container, false)
        binding.lifecycleOwner = viewLifecycleOwner
        binding.viewModel = viewModel
        viewModel.fetchText()
        return binding.root
    }
}
```

以上。

## 最後に
基本的な「MVVM  + LiveData + DataBinding」を実装しました。
最終的に双方向にしたので、LiveDataは関係なくなってしまいましたが。。。

また、ViewModelからRepositoryを直接読んでいますが、これでは依存しているため、
RepositoryのInterfaceを作り、ViewModelのコンストラクターでそのRepositoryのIntefaceを渡すようにするとなお良くなる。

###GitHub
勉強会のカンペ用に色々無駄なコードが書かれちゃっているけど
https://github.com/mk2taiga/MVVMStudy

## 参考にした記事
https://qiita.com/Omoti/items/a83910a990e64f4dbdf1
https://qiita.com/takaaki7/items/91d34e8bf9ad5d71ddd2