View BindingはViewを操作するコードを簡単に記述できる機能で、XMLレイアウトごとBindすることができます。
View Binding が「大概のケース」でfindViewByIdの後継として使えます。例外もありまったりしますが…
2021.1.1
View Binding の設定
XMLレイアウトファイルからIDを読みだして、findViewById を使ってコードと結びつけるのが昔からの方法です。
findViewById の不都合なことがある場合として、
- nullable
- findViewByIdの戻り値はnullableでタイミングが悪いあるいは間違ったidを読んでnullなる可能性が潜んでいる
- 型の指定
- 型安全ではないので型を指定して呼び出す必要がある
- 紐づけが面倒
- たくさんの紐づけをする場合に顕著に表れる冗長的な作業、バグの元
等があります。1つ2つであればfindViewById は簡単ですが大きなプロジェクトでは
View Binding が推奨されるでしょう。
Kotlinでは以下で説明しています。
build.gradleの設定
build.gradle ファイルに viewBinding 要素を追加し true にします。
同じ名前のファイルが2つあり、プロジェクト用(Project: xxx)とモジュール用(Module: xxx.app)で、モジュール用を使用します。
ターゲットのAPIレベルやライブラリーの情報が設定されます。
build.gradle(Module: xxx)
1 2 3 4 5 6 7 8 |
... android { ... buildFeatures { viewBinding true } } ... |
この他に以下のような設定でも通りました。
1 2 3 |
viewBinding { enabled = true } |
あるいは、gradle.properties に以下のような記述を追加しても可能です
1 |
android.defaults.buildfeatures.viewbinding=true |
設定後に「Sync Now」を実行するのを忘れずに
また反映されるのに時間がかかることもあります
Binding Classを生成
プロジェクトのレイアウトファイル毎にViewBinding Class を作成します。
1 2 3 |
package com.example.testbutton ... import com.example.testbutton.databinding.ActivityMainBinding |
例えば、activity_main.xml の場合はactivity_mainをキャメルケースに変換して、package name とdatabindingを頭に付け、末尾に「Binding」を追加することで生成します。
[package name].databinding.[XMLレイアウトファイル名から]Binding
activity_main.xml | >> | ActivityMainBinding |
sub_layout.xml | >> | SubLayoutBinding |
main.xml | >> | MainBinding |
activity_main.xmが以下のような場合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout ... > <TextView android:id="@+id/text_view" ... /> <ImageView android:src="@drawable/image" ... /> <EditText android:id="@+id/edittext" .../> <Button android:id="@+id/button" ... /> </androidx.constraintlayout.widget.ConstraintLayout> |
Binding Class のActivityMainBindingには3つのフィールドがあります。
- text_view という名前の TextView フィールド
- edittext という名前の EditText フィールド
- button という名前の Button フィールド
bindingで以下のように参照できます
1 2 3 4 5 |
binding.textView... binding.edittext... binding.button... |
ImageViewには ID が無いので、このバインディングクラス内にそのビューへの参照は存在しません。
Binding Classには getRoot() メソッドも含まれるので、レイアウトファイルの
root viewを直接参照できます。上記のActivityMainBindingクラスでは getRoot() により
constraintlayout の root view を返します。
Activityへの実装
MainActivityの onCreate() で次の手順を実施
- Binding Classのインスタンスを生成
- Binding Classに含まれる静的 inflate() メソッドを呼び出し生成
- getRoot()を呼び出してroot viewへの参照を取得
- root view を setContentView() に渡す
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// View Binding Class のインスタンス // lateinit で宣言し初期化タイミングを onCreate() まで遅らせる private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // bindingのイニシャライズ // Binding Classに含まれる静的 inflate() メソッドを呼び出す binding = ActivityMainBinding.inflate(getLayoutInflater()); // root view への参照を取得 View view = binding.getRoot(); // view をsetContentView()にセット setContentView(view) binding.button.setOnClickListener(new View.OnClickListener() { ... }); } |
冗長なので短縮できます
1 2 |
// view をsetContentView()にセット setContentView(binding.getRoot()); |
以上で設定ができました。
サンプルコード
ButtonとTextViewを使ったJavaコードのサンプルをView Bindingにしてみます。
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
//package com.example.testbuttonviewbinding; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import com.example.testbuttonviewbinding.databinding.ActivityMainBinding; public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; private boolean buttonTap = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.activity_main); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); binding.button.setOnClickListener( v -> { // flagがtrueの時 if (buttonTap) { binding.textView.setText(R.string.hello); buttonTap = false; } // flagがfalseの時 else { binding.textView.setText(R.string.world); buttonTap = true; } }); } } |
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="20dp" tools:context=".MainActivity"> <TextView android:id="@+id/text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello" android:textSize="50sp" android:textColor="#00ff00" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.4" /> <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/button" android:textSize="20sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.6" /> </androidx.constraintlayout.widget.ConstraintLayout> |
strings.xml
1 2 3 4 5 6 |
<resources> <string name="app_name">TestButtonViewBinding</string> <string name="hello">Hello</string> <string name="world">World</string> <string name="button">Button</string> </resources> |
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
plugins { id 'com.android.application' } android { ... buildFeatures { viewBinding true } } dependencies { ... } |
References:
ビュー バインディング – Android Developers
findViewById
BuildFeatures | Android Developers