【Android-Codelabs】9/36 〜 DataBindingの使い方 〜



2019年にGoogleから，Web上でAndroidを学べるコースが提供されました．([Android Kotlin Fundamentals Course](https://codelabs.developers.google.com/android-kotlin-fundamentals/))
この記事では，そのコースを実際にやってみてアウトプットしてみるといった内容です．
何かツッコミなどあれば編集リクエストかコメントかTwitterでいただければ修正いたします :pray:

#今回学ぶこと
・DataBindingとはなにか
・DataBindingの使い方
・DataBindingを使うと，findViewById()よりも効率的なビューの呼び出しができるということ

これまでは，MainActivity.ktでビューを参照する場合，`findViewById()`関数を使っていました．
シンプルなアプリであればこれでも良かったのですが，複雑なビュー階層がある場合，`findViewById()`はルートから探索を開始して，目的のビューが見つかるまで探索を続けるので，アプリの速度が低下します．

そんな問題を解決するのが`DataBinding`という技術です．
まず，各ビューへの参照を含むオブジェクトを作成することです．
アプリのバインディングオブジェクトが作成されると，ビュー階層を探索したりするこなく，バインディングオブジェクトを介してビューや他のデータにアクセスすることができます．

![2-4-2.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249786/491b27aa-77a9-fef2-acc0-c461057f45e4.jpeg)


`DataBinding`には次のメリットがあります．

・`findViewById()`に比べてコードが短く，読みやすく，保守しやすいこと
・データとビューが明確に分離されていること
・各ビューを取得するために，ビュー階層を１度だけ全て探索すること
（ただし，これはユーザーがアプリを操作している時ではなくアプリの起動時に行われます．）
・ビューにアクセスするための，TypeSafeを取得すること
（TypeSafeとは，コンパイラがコンパイル中に，型を検証し変数に間違った型を割り当てようとすると，エラーが渡されることです）


#目指す成果物

ここでは，以前作ったAboutMeというアプリに，`DataBinding`という技術を実装していきます．
なので，完成したら見た目は同じです．

<img width="606" alt="2-4-1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249786/970ecbdb-5e24-01f6-099c-75e6357be293.png">


アプリの機能：
・ユーザーがアプリを開くと，名前，ニックネームを入力するフィールド，DONEボタン，スクロール可能なテキストが表示される
・ユーザーはニックネームを入力して，DONEボタンをタップします．
ニックネームを入力するフィールドとDONEボタンは，ニックネームを表示するテキストビューに置き換わります．

アプリがない場合は，ソースコードを[ここ](https://github.com/syuheifujita/android-codeLab-fundamental-2-2)からダウンロードしてください．

#ステップ
###1. DataBindingを使って，findViewByID()を無くす

ここでは，DataBindingをセットアップし，`findViewById()`の呼び出しを`DataBinding`を使っての呼び出しに置き換えていきたいと思います．

まず，GradleファイルでDataBindingを有効にする必要があります．（デフォルトでは有効になっていません）
これは，コンパイル時間が長くなりアプリの起動時間が影響を受ける可能性があるためです．

`build.gradle(Module: app)`ファイルを開きます．
そして，android{}セクションにこちらを追加します．

```
dataBinding {
    enabled = true
}
```

コードを追加できたら，`Sync`（同期）をしてください．
`File`>`Sync Project with Gradle Files`をクリックします．

次に，DataBindingを使用するには，XMLレイアウトを`<layout>`タグでラップする必要があります．

```activity_main.xml
<layout>
   <LinearLayout ... >
   ...
   </LinearLayout>
</layout>
```

`layout`には以下のタグが含まれてる必要があります．

```
<layout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
```

次にバインディングオブジェクトの変数をMainActivity.ktに追加し，それを使ってビューにアクセスできるようにします．
onCreate()の前に，こちらの変数を定義します．`ActivityMainBinding`クラスは，コンパイラによってこのMainActivity専用に作成されます．
この名前は，レイアウトファイルの名前は`activity_main + Binding`から派生しています．

```
private lateinit var binding: ActivityMainBinding
```

次に，`DataBindingUtil`クラスの`setContentView()`関数を使って，MainActivity.ktをactivity_main.xmlに紐付けます．
onCreate()のsetContentView()の呼び出しをこのようなコードに置き換えます．

```
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
```

これで，`findViewById()`の全ての呼び出しをバインディングオブジェクトに置き換えることができます．
バインデングオブジェクトが生成されると，コンパイラはレイアウト内のビューのIDからバインディングオブジェクト内のビューの名前を生成し，それらをキャメルケースに変換します．
例えば，`done_button`は`doneButton`がバインデングオブジェクトにあたります．

クリックリスナーを実装するには，こちらのようにします．

```
binding.doneButton.setOnClickListener {
   addNickname(it)
}
```

他の箇所も同じように修正してみてください．

そして，アプリを実行してみて以前と動作が変わらないことを確認してみてください．


###2. ビューにデータをバインドして，データを表示させる

`DataBinding`では，`データクラス`を直接使うこともできます
この手法はコードを，より簡素化し，より複雑なケースに非常に役に立つそうです．

ここでは，文字列リソースを使って名前とニックネームを設定する代わりに，名前とニックネームの`データクラス`を作成します．
DataBindingを使って，データクラスをビューで使えるようにしましょう．

まず，データクラス「MyName.kt」を作成します．
新しいKotlinファイルを作成しましょう．

データクラスはこのように定義して，デフォルト値としてからの文字列を使います．

```
data class MyName(var name: String = "", var nickname: String = "")
```

次に，activity_main.xmlファイルの`<layout>`タグと`<LinearLayout>`タグの間に、`<data></data>`タグを挿入します．
これはビューとデータを接続する場所で，データタグ内で，クラスへの参照を保持する名前付き変数を定義できます．

次に，<data>タグ内にこちらを追加します．

```
<variable
       name="myName"
       type="com.example.android.aboutme.MyName" />
```
name変数に名前をつけるパラメーターを用意します．
Typeパラメーターを追加し，MyNameクラスの完全修飾名にtypeを設定します．
これで、名前に文字列リソースを使用する代わりに、myName変数を参照できるようになりました。

次に，`android:text="@string/name”`を以下のコードに置き換えます．
`@={} `は括弧内で，参照されるデータを取得するためのものです．

```
android:text="@={myName.name}"
```

これで，レイアウトファイルのデータへの参照が
次に，実際のデータを作成します．

`MainActivity.kt`ファイルで，private変数を定義します．
変数にデータクラス「MyName」のインスタンスを割り当てて名前を渡します．

```
private val myName: MyName = MyName("Aleks Haecky")
```

そして，onCreate()内で，こちらを定義して，宣言したmyName変数に値を設定します．
XMLファイルの変数には，直接アクセスすることができないので，バインデングオブジェクトを介してアクセスします．

```
binding.myName = myName
```

次に，TextViewのニックネームにもデータクラスを使ってみます．

```
android:text="@={myName.nickname}"
```

そして，MainActivity.ktもこのように変更します．

```
myName?.nickname = binding.nicknameEdit.text.toString()
```

ニックネームを設定したら、MainActivity.ktで新しいデータでUIを更新する必要があります．
これを行うには，正しいデータで再作成されるように，すべてのバインディング式を無効にする必要があります．

ニックネームを設定した後にinvalidateAll()を追加して，更新されたバインディングオブジェクトの値でUIが更新されるようにします．

```
binding.apply {
   myName?.nickname = nicknameEdit.text.toString()
   invalidateAll()
   ...
}
```

アプリを実行してみましょう．以前と同じく動作したら成功です．

#まとめ
・findViewById()の代わりに，DataBindingを使うと，レイアウトXMLファイルから直接データにアクセスできる


#クイズ
>1問目
findViewById()の呼び出しを最小限にしたいのはなぜですか？

・findViewById()呼び出されるたびに、毎回ビュー階層を横断するから
・findViewById() メインスレッドまたはUIスレッドで実行されるから
・この呼び出しは、ユーザーインターフェイスの速度を低下させる可能性があるから
・アプリがクラッシュする可能性は低くなるから

>2問目
DataBindingに関して正しい説明はどれですか？

・コンパイル時に2つの離れた情報を接バインドするオブジェクトを作成します．これにより実行時にデータを探す必要がなくなります。
・バインディングを表示するオブジェクトは，バインディングオブジェクトと呼ばれる
・バインディングオブジェクトは、コンパイラによって作成される

>3問目
次のうち、DataBindingのメリットではないものはどれですか？

・コードは短く、読みやすく、保守が簡単である
・データとビューは明確に分離されている
・各ビューを取得するために，ビュー階層を1回だけ探索する
・findViewById()の呼び出しは，コンパイラエラーを生成する
・Type safetyはビューにアクセスするためのである

4問目>
`<layout>`タグの機能は何ですか？

・レイアウト内のルートビューをラップします
・レイアウト内のすべてのビューに対してバインディングが作成されます
・DataBindingを使用するXMLレイアウトの最上位ビューを指定します
・`<data>`内でタグを使用して`<layout>`変数をデータクラスにバインドします

5問目>
XMLレイアウトでバインドされたデータを参照する正しい方法はどれですか？

・`android:text="@={myDataClass.property}”`
・`android:text="@={myDataClass}”`
・`android:text="@={myDataClass.property.toString()}”`
・`android:text="@={myDataClass.bound_data.property}”`


###クイズの正解

1問目　この呼び出しは、ユーザーインターフェイスの速度を低下させる可能性があるから
2問目    
3問目　Type safetyはビューにアクセスするためのである
4問目　DataBindingを使用するXMLレイアウトの最上位ビューを指定します。
5問目　`android:text="@={myDataClass.property}”`

#参考資料
###ソースコード
Github:
https://github.com/syuheifujita/android-codeLab-fundamental-2-4

###言葉の定義
・DataBinding（データバインディング）
findViewById()よりも効率的なビューの呼び出しができる技術
アプリのバインディングオブジェクトを作成して，ビュー階層を探索したりするこなく，バインディングオブジェクトを介してビューや他のデータにアクセスすることができるような技術

・Binding Object（バインディングオブジェクト）
DataBindingが実装されたときに生成されるオブジェクトのことで，各ビューへの参照を含みます

・データクラス
strings.xmlを介しての文字列データのやり取りの代わりに，データクラスを使って値の受け渡しをします
（より複雑なアプリの時に，非常に役に立つそうです．）

###CodeLabs by Google
https://codelabs.developers.google.com/android-kotlin-fundamentals/
![スクリーンショット 2020-02-05 4.07.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249786/3ad2d3c9-d57f-1b4b-85cf-1c92a57b464b.png)