Google codelabs Android DataBinding のコードのつながりをふわ～っと確認

最近、Databindingを理解するために、[Google codelabs Android DataBinding](https://developer.android.com/codelabs/android-databinding#0)で学習してみたのですが、~~思考停止で~~書かれている通りに実装したところ、Databindingは実装できたものの、**どのコードと、どのコードが関連して動作しているのかが、いまいちピンときていなかったので**、コード同士のつながりを確認することにしました。
この記事はその確認結果のまとめです。

#Google codelabs Android DataBindingで作るもの
このCodelabsでは以下のようなものを作ります。（ほぼ完成しているものが準備されている）
![アプリ動作.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599060/501929d5-b208-1749-774e-5e827a532d58.gif)

LIKEボタンをポチポチするとLikesの数字が増えて、Likesが一定以上になると、プログレスバーとアイコンが変化します。
最初から準備されているコードは、DataBindingを使用せずにこの挙動を実現している状態なので、それを修正してDataBindingで実現する、というのがこのCodelabsの主旨です。

#Codelabsの内容
全力でサボりますが、[こちら](https://qiita.com/kasa_le/items/ce3c261b002fb817064a)の記事で全編内容が紹介されています。分かりにくい部分（Codelabs内のケアレスミスとか）も注釈として書かれてるので参考になりました。

#コードのつながりを読み解く
読み解くというほどのものでもないかもですが、読み解きます笑
※ここで扱うコードは、Codelabsの最初に提供されるものではなく、Codelabsが完了した段階でのものです。

かなりざっくり言うと、このアプリは
・PlainOldActivity.kt
・SimpleViewModel.kt
・BindingAdapter
・plain_activity
で構成されています。

これらの中から、DataBindingの構成要素を独断と偏見で抜き出すと、こんな概略図になります。（いろいろ省略してます）

![主要クラス構成図png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599060/b7040f37-0d1e-8d1f-9b6f-f93e20f27071.png)
これらのつながりを、ざっくり見ていきます。

---
まず、plain_activity.xml内でレイアウト変数（layout variables）が定義されています。

```plain_activity.xml
   <data>
       <variable
            name="viewmodel"
            type="com.example.android.databinding.basicsample.data.SimpleViewModel"/>
   </data>
```
nameは**viewmodel**となっていて、
typeは**"com.example.android.databinding.basicsample.data.SimpleViewModel"**
を指定しており、これは**SimpleViewModel.kt**のことを指しています。

なので意味的には、「viewmodelは、SimpleViewModel.ktのことを指す」という具合になりそうです。

続いてタグ内で
**android:text="@{}"**というような表記 (layout expression、レイアウト式) が出てきます。
ここにdataタグ内で記述したレイアウト変数が入ります。
こんな感じ↓

```plain_activity.xml
<TextView
            android:id="@+id/plain_name"
            android:text="@{viewmodel.name}"    
以下省略     
/>
```
ここでdataタグ内のnameで定義した、**viewmodel**が**@{viewmodel.name}**という形で記述されています。
これはデータの紐付け先を示しています。

viewmodelは**SimpleViewModel.kt（com.example.android.databinding.basicsample.data.SimpleViewModel）**を型として指定していたので、「SimpleViewModel.ktからnameを引っ張って来い」というように読めます。

ただ、plain_activity.xmlからSimpleViewModel.ktへ直接紐付けされているわけでありません。
PlainOldActivity.ktは以下のようになっています。

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

    // Obtain ViewModel from ViewModelProviders
    private val viewModel by lazy { ViewModelProviders.of(this).get(SimpleViewModel::class.java) }

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

        //➀
        val binding : PlainActivityBinding =
                DataBindingUtil.setContentView(this, R.layout.plain_activity)

        binding.lifecycleOwner = this
        
        //➁
        binding.viewmodel = viewModel
    }
}

```
➀はbinding先のレイアウト(plain_activity.xml)が定義されています。

➁は**binding.viewmodel = viewModel**と定義されています。
binding.viewmodelの”viewmodel”は、plain_activity.xmlで定義したviewodelのことを指しています。

一方、右辺のviewModel(※Mが大文字なので”viewmodel”とは異なる)はSimpleViewModel.ktのViewModelを、 ViewModelProvidersで受け取ったものです。

なので、binding.viewmodel = viewModelは
**plain_activity.xmlとSimpleViewModel.ktのViewModel**の紐付けを表しています。

ここまでの関係図を矢印で書くと、こんな感じになります。↓
![主要クラス構成図(矢印追加後)png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599060/86b56159-d497-b1ab-c10a-cb0801cb35ba.png)



plain_activity.xmlとSimpleViewModel.ktが紐づけされた状態なので、
**android:text="@{viewmodel.name}**"や、**app:hideIfZero="@{viewmodel.likes}"****のように、レイアウトからSimpleViewModel.ktの変数(name, likes)を引用することができます。

例えばnameは、_nameが代入されており、_nameは**MutableLiveData("Ada")**として定義されているので、Nameのところに”Ada”が表示されます。
![アプリ画面（Nameに赤枠付き）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599060/078fa778-5428-d7d0-3750-a93188400cb8.png)

##BindingAdapter
さて、残りのBindingAdapter.ktでは、plain_activity.xmlが受け取った変数を引数とするメソッドが定義されています。以下に一例を挙げます。

```BindingAdapter.kt
@BindingAdapter("app:popularityIcon")
fun popularityIcon(view: ImageView, popularity: Popularity) {

    val color = getAssociatedColor(popularity, view.context)

    ImageViewCompat.setImageTintList(view, ColorStateList.valueOf(color))

    view.setImageDrawable(getDrawablePopularity(popularity, view.context))
}
```

```plain_activity.xml
<ImageView
app:popularityIcon="@{viewmodel.popularity}"
以下省略
/>
```
この場合だと**＠BindingAdapter(app:popularityIcon)**という表記で、popularityIconメソッドが、plain_activity.xmlのImageViewと紐付けされています。

メソッドの引数の型は、**(ビューの型,レイアウト側から受けとる変数の型)**となっています。
上の例だと、fun popularityIcon **（view: ImageView, popularity: Popularity）**のようになっていて、渡されたpopularityの中身によってメソッドの処理結果が変わるため、ImageViewの画像表示が切り替わるようになっています。

これで、SimpleViewModel.ktからBindingAdapter.ktまでのつながりが（ふわ～っと笑）確認できました。
![主要クラス構成図(レイアウトとBindingAdapterの紐づけ 全体図)png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599060/93409ab4-49cd-e454-a92d-95069dc4d99b.png)

BindingAdapterの部分だけ抜き出した場合はこんな感じ↓
![主要クラス構成図(レイアウトとBindingAdapterの紐づけ)png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599060/f03d56e5-cd9d-bc47-c8ea-9e05c90e0d37.png)


##ボタンタップからUI更新の流れ
このアプリはLIKEボタンがトリガーになっていて、LIKEボタンのタップ回数によって画像や色が変わっていきますが、どういう挙動になっているのか確認するために、流れをまとめました。
（図はなんやかんや省略してます。"イメージをつかむ"程度の認識で眺めてください）

###LIKEボタンのタップ
・LIKEボタンを押すと、ボタンがSimpleViewModelのonLike()メソッドと紐付いているので、_likesが増える。
↓
・_likesはlikes、popularityの二つのLiveDataに代入されるので、2つに分岐する。

![LIKEボタン押したときの分岐.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599060/8b1be7db-dfc3-066e-6b9b-e4a3843dc0a5.png)



##likes側
➀TextViewがlikesを受け取り、String型に変換し、タップ数として表示する。


➁プログレスバー(ProgressBar)は2つの＠BindingAdapterと紐づいているので分岐する。
　➁-1 likesが0ならばプログレスバーを非表示、それ以外の場合は表示する。
　➁-2 likesの数量に応じてプログレスバーの進捗具合を表示する。

![LIKEボタン押したときの分岐(変数_likes).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599060/76db1289-215e-3f79-4f3b-0b82045cf231.png)


##popularity側
Transformations.mapで_likesがPopularity型へ変換され、when式でPopularityレベルを分ける。
↓
➀、➁共にSimpleViewModel.ktからpopularityを受け取り、＠BindingAdapterで紐づいたメソッドへ、引数として渡す。
➀はImageViewの画像の切り替え、➁はプログレスバーの色変更をする。

![LIKEボタン押したときの分岐(popularity).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599060/66b2eb0d-5980-f00b-0c97-180b5eccd28a.png)

##感想
まとめるのにだいぶ時間がかかってしまいましたが、概略図がなんとなくイメージできるようになったので、やった意味はあったと思います。実際にたくさん使って、何か作ってみようと思います～^^