Sunflowerリポジトリで学ぶJetPack〜DataBinding編


新型コロナの影響で自宅待機になってしまい、その間勉強するものとして`Sunflower`リポジトリを
勧めてもらいました。

JetPackのライブラリのうち、今回は`DataBinding`編です

尚、引用しているソースは明記しているところ以外は、全て`Sunflower`のリポジトリのものです。　

# 環境
* 確認時は`Android Studio`のバージョンは `3.6.2`を使用
* `Data binding`はサポートライブラリなので、`Android 4.0`（API レベル 14)以降で利用可能
* `Android Plugin for Gradle`の`1.5.0`以降でサポートされているが、なるべく最新を使用したほうがいい（らしい）

# DataBinding を利用するのに必要なこと
* ビルド環境設定
* レイアウトファイルを変更
* 購読処理の実施(LiveDataとの連携)

# 準備
`Build.gradle`ファイルで`Data Binding`の機能を有効にします
`Sunflower`では`app/build.gradle`に記載してありますねー!(^^)

```gradle:app/build.gradle

android {
    compileSdkVersion rootProject.compileSdkVersion
    dataBinding {
        enabled = true
    }
```

# レイアウトファイル
ここでは`Data Binding`を利用している`fragment_garden.xml`を見てみましょう

```XML:fragment_garden.xml
<layout 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">

    <data>

        <variable
                name="hasPlantings"
                type="boolean" />

    </data>
    <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/garden_list"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:clipToPadding="false"
                android:layout_marginStart="@dimen/card_side_margin"
                android:layout_marginEnd="@dimen/card_side_margin"
                android:layout_marginTop="@dimen/margin_normal"
                app:isGone="@{!hasPlantings}"
                app:layoutManager="androidx.recyclerview.widget.StaggeredGridLayoutManager"
                app:spanCount="@integer/grid_columns"
                tools:listitem="@layout/list_item_garden_planting"/>

        <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:orientation="vertical"
                app:isGone="@{hasPlantings}">

                :
                :

        </LinearLayout>

    </FrameLayout>

</layout>
```

## ポイント
1. レイアウトファイルが`<Layout>タグ`で開始されている。（`<LinearLayout>`とかじゃない）
2. その中に`<data>タグ`がある。
3. さらにその中に`<variable>タグ`でバインディング変数を定義している
4. バインディング変数は`name`で名前を、`type`で型を定義している
5. バインディング変数の利用箇所は`@{変数名}`と記載する
   ここでは、`"@{hasPlantings}"`と記載している
*ちなみに`"@{viewModel.imageUrl}"`のようなプロパティ参照もできる（`list_item_garden_planting.xml`参照)

ここでは`hasPlantings`の値によって`RecyclerView`となにもない時に表示する`LinearLayout`を出し分けてますね

# データのバインド

ではレイアウトファイルで記載した変数をどう関連付けるのか、`Kotlin`ファイルを確認してみましょう

```kotlin:GardenFragment.kt
class GardenFragment : Fragment() {

 private lateinit var binding: FragmentGardenBinding
          :
          :
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentGardenBinding.inflate(inflater, container, false)
        val adapter = GardenPlantingAdapter()
        binding.gardenList.adapter = adapter

        binding.addPlant.setOnClickListener {
            navigateToPlantListPage()
        }

        subscribeUi(adapter, binding)
        return binding.root
    }
          :
          :
}
```





## ポイント
１. データバインディングを実施すると、バインディングクラスがレイアウトファイルごとに作られる。  作られるクラスの名称は下記のルールで決定されます

```text
バインディング クラスはレイアウト ファイルごとに生成されます。
デフォルトのクラス名は、レイアウト ファイルの名前がパスカルケースに変換され、Binding サフィックスが付加されたものになります。
```

**→**ここの場合は`fragment_garden.xml`のレイアウトファイルから`FragmentGardenBinding`クラスが作成されます


２. データバインディングの変数を利用するために、`1.`で自動生成されたバインディングクラスで`inflate()`します。
３. `Fragment`、`ListView`、`RecyclerView`などいずれかのアダプター内でデータバインディングの変数を利用する場合は、**`DataBindingUtil#inflate()`**メソッドも利用可能です

### `DataBindingUtil#inflate()`利用箇所
`GardenPlantingAdapter`では、`DataBindingUtil#inflate()`メソッドを利用してバインディングしていました。

```kotlin:GardenPlantingAdapter.kt
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
            DataBindingUtil.inflate(
                LayoutInflater.from(parent.context),
                R.layout.list_item_garden_planting, parent, false
            )
        )
    }
```

# 購読処理の実施(LiveDataとの連携)
それでは、購読する処理を見てみましょう

```kotlin:GardenFragment.kt
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentGardenBinding.inflate(inflater, container, false)
          :
          :
        subscribeUi(adapter, binding)
        return binding.root
    }

    private fun subscribeUi(adapter: GardenPlantingAdapter, binding: FragmentGardenBinding) {
    viewModel.plantAndGardenPlantings.observe(viewLifecycleOwner) { result ->
        binding.hasPlantings = !result.isNullOrEmpty()
        adapter.submitList(result)
    }
```
名前の通り`subscribeUi()`メソッドで購読処理を実施しています。
なるほどー。 ここでは、実際にレイアウトにバインディングした`hasPlantings`を直接購読しているのではなく、その情報を算出するための`plantAndGardenPlantings`を`observe`しているんですね！
こうすることで、`plantAndGardenPlantings`の値が変更された時にresultが更新されて、そこから算出された
`hasPlantings`で`RecyclerView`を更新すると。

ここから先は[LiveData編](https://www.yahoo.co.jp)に記載します！


# 参考サイト
* [Sunflowerリポジトリ](https://github.com/android/sunflower)
* [DataBinding](https://developer.android.com/topic/libraries/data-binding): 公式ドキュメント
* [LiveData](https://developer.android.com/topic/libraries/architecture/livedata?hl=ja): :公式ドキュメント