AndroidでViewBindingを使ってみる

# ViewBindingとは・・・？なんじゃんねぇ？
AndroidではLayout xmlとJavaかkotlinのソースが別々で、Layout xmlでは各Viewにidを振ります。
```xml
android:id="@+id/xxxbutton"
```
それをJavaかkotlinのソース側から
```kotlin
val button = findViewById(R.id.xxxbutton)
```
といった風に参照します。AndroidではkotlinだとViewBindingという方法が使えます。

参考：
公式Android developers [ビューバインディング](https://developer.android.com/topic/libraries/view-binding)
# ViewBindingのメリットは？
findViewById と ViewBindingを比較した場合のメリットは
* null の安全性が高まる。
  * 無効なビュー ID によって NullPointerException が発生することがない。findViewByIdはidを間違えるとnullが返ってきます。(idはInt型であればなんでもいい)
* 型の安全性
  * XML ファイル内で参照しているビューがclassに変換され、型を持ちます。バインディングクラス内のフィールドは、レイアウトファイルの型と一致します。そのため、ClassCastException が起きることがない。(無理に違う型に操作しようとすると、コンパイル時にわかる)

大きく分けて以上の2点のメリットが有ります。googleもViewBindingはfindViewByIdの後継として推奨しているようです。
## ここで以前から勘違いしていたことに気がつく。
layout xmlのidですが、AndroidStudioが他のlayout xmlで使っているidと重複していると、警告を出してくるので
![Screenshot_20221128_210427.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933408/3f21c470-048e-8ae0-5eb3-649b9a1a289b.png)
今まで、idは全てのlayout xmlを通してユニークでなければならないんだと思っていました。(なので、idの名前の付け方が悩ましかった・・・)
R.id.xxxと、いった具合に、R.idの下にフラットにできるのでそうなんだろうなと思っていました。
ところが、idはlayout xmlを通して重複していても何ら問題ありません。ちゃんと動きます。(じゃ、何でAndroidStudioはあんな警告出すんだ？)
オマケに同一のlayout xmlの中で重複していても、ビルドエラーにもなりません。動きます。ただ、おそらく、実行時に先に見つけた方のViewのidで動いているんだと思います。

なので・・・
ViewBindingなると、idの重複の制約がなくなって、やりやすくなった・・・と思ってましたが、それはfindViewByIdもViewBindingも、一緒でした。
# ViewBindingを使えるようにするためには
ViewBindingはデフォルトでは有効になっていません。build.gradleに追記する必要があります。公式ホームページにもありますが2通りの指定方法があります。
```gradle:build.gradle
android {
   （中略）
    viewBinding {
        enabled = true
    }
}
```
```gradle:build.gradle
android {
   （中略）
    buildFeatures {
        viewBinding true
    }
}
```
僕はいつも上のやり方でやっています。修正してgradle同期すると使えるようになります。同期すると、layout xmlのファイル名に対応したclassができあがります。今、2つあるlayout xmlに対して
![Screenshot_20221128_212208.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933408/2f8b4cc1-614f-50e8-81fd-40d9d997eae8.png)
buildの下に2つのクラスができています。
![Screenshot_20221128_212114.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933408/b479dfcb-c1f1-d39a-5f7c-3131ba3e5343.png)
ネーミングはだいたい想像が付きますが、layout xmlのファイル名はキャメルケースに変換して、その後ろにBindingが付きます。
# ActivityでViewBindingを使う場合
ActivityでそれはfindViewByIdの場合は
```kotlin
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val txt: TextView = findViewById(R.id.helloTxt)
    }
}
```
ViewBindingだと
```kotlin
class MainActivity : AppCompatActivity() {
     lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        val txt = binding.helloTxt
    }
}
```
といった書き方になります。
# FlagmentでViewBindingを使う場合
Flagmentの場合はちょっと複雑です。findViewByIdの場合は,
```kotlin
class BlankFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_blank, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val txt: TextView = view.findViewById(R.id.helloTxt)
    }
}
```
ViewBindingだと
```kotlin
class BlankFragment : Fragment() {
    private var _binding: FragmentBlankBinding? = null
    private val binding get() = _binding!!
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentBlankBinding.inflate(inflater, container, false)
        return binding.root
    }
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val txt = binding.helloTxt
    }
    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}
```
といった書き方になります。
# で、結局、何が嬉しいの？
これだけを見るとViewBindingの方が若干、長くなっているんじゃないの？と思うかもしれませんが、上に上げた2つのメリットである、null の安全性、型の安全性を考えると使う価値はあるかと思います。
ViewBindingはキャッシュしてくれるので、findViewByIdよりも若干性能がいいという話も有ります。

簡潔かどうかという麺では
```kotlin
val txt: TextView = findViewById(R.id.helloTxt)
```
```kotlin
val txt = binding.helloTxt
```
ここだけを見ると、ViewBindingの方が簡潔であると言えます。(無理あるかなー)
ボタンのイベントリスナを設定する場合とかは
```kotlin
binding.button.setOnClickListener {
    ・・・
}
```
で一気に書いちゃう場合が多いです。