ビュー バインディング Android Jetpack の一部
ビュー バインディングは、ビューを操作するコードを簡単に記述できる機能です。モジュール内でビュー バインディングを有効にすると、そのモジュール内に存在する XML レイアウト ファイルごとにバインディング クラスが生成されます。バインディング クラスのインスタンスには、対応するレイアウトで ID を持つすべてのビューへの直接参照が入っています。
ほとんどの場合、ビュー バインディングは findViewById
の後継となります。
設定
ビュー バインディングは、モジュール単位で有効化します。モジュールでビュー バインディングを有効にするには、次の例に示すように、モジュール レベルの build.gradle
ファイルで viewBinding
ビルド オプションを true
に設定します。
Groovy
android { ... buildFeatures { viewBinding true } }
Kotlin
android { ... buildFeatures { viewBinding = true } }
バインディング クラスの生成中にレイアウト ファイルを無視する場合は、そのレイアウト ファイルのルートビューに tools:viewBindingIgnore="true"
属性を追加します。
<LinearLayout
...
tools:viewBindingIgnore="true" >
...
</LinearLayout>
使用方法
モジュールに対してビュー バインディングを有効にすると、そのモジュールに含まれる XML レイアウト ファイルごとにバインディング クラスが生成されます。各バインディング クラスには、ルートビューへの参照と、ID を持つすべてのビューへの参照が組み込まれます。バインディング クラスの名前は、XML ファイルの名前を Pascal ケースに変換して、末尾に「Binding」という単語を追加することで生成されます。
たとえば、次の内容を含む result_profile.xml
というレイアウト ファイルがあるとします。
<LinearLayout ... >
<TextView android:id="@+id/name" />
<ImageView android:cropToPadding="true" />
<Button android:id="@+id/button"
android:background="@drawable/rounded_button" />
</LinearLayout>
生成されるバインディング クラスは、ResultProfileBinding
という名前になります。このクラスには、2 つのフィールドがあります。name
という名前の TextView
フィールドと、button
という名前の Button
フィールドです。レイアウト内の ImageView
には ID がないため、バインディング クラス内にそのビューへの参照は存在しません。
各バインディング クラスには getRoot()
メソッドも含まれ、対象レイアウト ファイルのルートビューを直接参照します。この例では、ResultProfileBinding
クラスの getRoot()
メソッドは、LinearLayout
ルートビューを返します。
以下のセクションでは、アクティビティとフラグメントでの、生成されたバインディング クラスの使用について説明します。
アクティビティでビュー バインディングを使用する
アクティビティで使用するバインディング クラスのインスタンスをセットアップするには、アクティビティの onCreate()
メソッドで次の手順を実施します。
- 生成されたバインディング クラスに含まれる静的
inflate()
メソッドを呼び出します。これにより、アクティビティで使用するバインディング クラスのインスタンスが作成されます。 getRoot()
メソッドを呼び出すか、Kotlin プロパティ構文を使用して、ルートビューへの参照を取得します。- ルートビューを
setContentView()
に渡して、画面上のアクティブ ビューにします。
次の例はそれらの手順を示しています。
Kotlin
private lateinit var binding: ResultProfileBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ResultProfileBinding.inflate(layoutInflater) val view = binding.root setContentView(view) }
Java
private ResultProfileBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ResultProfileBinding.inflate(getLayoutInflater()); View view = binding.getRoot(); setContentView(view); }
これで、バインディング クラスのインスタンスを使用して任意のビューを参照できるようになりました。
Kotlin
binding.name.text = viewModel.name binding.button.setOnClickListener { viewModel.userClicked() }
Java
binding.name.setText(viewModel.getName()); binding.button.setOnClickListener(new View.OnClickListener() { viewModel.userClicked() });
フラグメントでビュー バインディングを使用する
フラグメントで使用するバインディング クラスのインスタンスを設定するには、フラグメントの onCreateView()
メソッドで次の手順を実施します。
- 生成されたバインディング クラスに含まれる静的
inflate()
メソッドを呼び出します。これにより、フラグメントが使用するバインディング クラスのインスタンスが作成されます。 getRoot()
メソッドを呼び出すか、Kotlin プロパティ構文を使用して、ルートビューへの参照を取得します。onCreateView()
メソッドからルートビューを返して、画面上のアクティブ ビューにします。
Kotlin
private var _binding: ResultProfileBinding? = null // This property is only valid between onCreateView and // onDestroyView. private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = ResultProfileBinding.inflate(inflater, container, false) val view = binding.root return view } override fun onDestroyView() { super.onDestroyView() _binding = null }
Java
private ResultProfileBinding binding; @Override public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = ResultProfileBinding.inflate(inflater, container, false); View view = binding.getRoot(); return view; } @Override public void onDestroyView() { super.onDestroyView(); binding = null; }
これで、バインディング クラスのインスタンスを使用して任意のビューを参照できるようになりました。
Kotlin
binding.name.text = viewModel.name binding.button.setOnClickListener { viewModel.userClicked() }
Java
binding.name.setText(viewModel.getName()); binding.button.setOnClickListener(new View.OnClickListener() { viewModel.userClicked() });
さまざまな構成に関するヒントを提供する
複数の構成にわたってビューを宣言する場合は、特定のレイアウトに応じて異なるビュータイプを使用するのが適切な場合があります。次のコード スニペットは、その例を示しています。
# in res/layout/example.xml
<TextView android:id="@+id/user_bio" />
# in res/layout-land/example.xml
<EditText android:id="@+id/user_bio" />
この場合、TextView
が共通の基底クラスであるため、生成されたクラスは TextView
型のフィールド userBio
を公開すると予想されます。技術的な制限により、ビュー バインディング コード生成ツールはこれを判断できず、代わりに View
フィールドを生成します。この場合、後で binding.userBio as TextView
を使用してフィールドをキャストする必要があります。
この制限を回避するため、ビュー バインディングは tools:viewBindingType
属性をサポートしており、生成されたコードで使用する型をコンパイラに指示できます。上の例では、この属性を使用して、コンパイラがフィールドを TextView
として生成するようにします。
# in res/layout/example.xml (unchanged)
<TextView android:id="@+id/user_bio" />
# in res/layout-land/example.xml
<EditText android:id="@+id/user_bio" tools:viewBindingType="TextView" />
別の例として、BottomNavigationView
を含むレイアウトと NavigationRailView
を含むレイアウトの 2 つがあるとします。どちらのクラスも NavigationBarView
を拡張します。このクラスには実装の詳細のほとんどが含まれています。現在のレイアウトに存在するサブクラスをコードで正確に把握する必要がない場合は、tools:viewBindingType
を使用して、両方のレイアウトで生成された型を NavigationBarView
に設定できます。
# in res/layout/navigation_example.xml
<BottomNavigationView android:id="@+id/navigation" tools:viewBindingType="NavigationBarView" />
# in res/layout-w720/navigation_example.xml
<NavigationRailView android:id="@+id/navigation" tools:viewBindingType="NavigationBarView" />
ビュー バインディングは、コード生成時にこの属性の値を検証できません。コンパイル時エラーと実行時エラーを回避するには、値が次の条件を満たしている必要があります。
- 値は
android.view.View
から継承するクラスである必要があります。 値は、配置先のタグのスーパークラスである必要があります。たとえば、次の値は機能しません。
<TextView tools:viewBindingType="ImageView" /> <!-- ImageView is not related to TextView. --> <TextView tools:viewBindingType="Button" /> <!-- Button is not a superclass of TextView. -->
最終的な型は、すべての構成で一貫して解決する必要があります。
findViewById との違い
ビュー バインディングには、findViewById
を使用するよりも大きなメリットがあります。
- null 安全性: ビュー バインディングはビューへの直接参照を作成するため、無効なビュー ID が原因で null ポインタ例外が発生するリスクはありません。また、ビューがレイアウトの一部の構成にのみ存在する場合、バインディング クラス内の参照を含むフィールドには
@Nullable
が付けられます。 - 型の安全性: 各バインディング クラス内のフィールドは、XML ファイル内で参照しているビューと合致する型を持ちます。そのため、ClassCastException のリスクがありません。
このような違いがあるため、レイアウトとコードとの間に互換性がない場合は、実行時ではなくコンパイル時にビルドが失敗することになります。
データ バインディングとの比較
ビュー バインディングと データ バインディングは、両方ともビューを直接参照できるバインディング クラスを生成します。ただし、ビュー バインディングはよりシンプルなユースケースを扱うことを目的としており、データ バインディングに比べて次のようなメリットがあります。
- コンパイルが高速: ビュー バインディングはアノテーション処理を必要としないため、コンパイル時間が短縮されます。
- 使いやすい: ビュー バインディングは特別にタグ付けされた XML レイアウト ファイルを必要としないため、より迅速にアプリで利用できます。モジュールでビュー バインディングを有効にすると、そのモジュールのすべてのレイアウトに自動的に適用されます。
一方、ビュー バインディングには、データ バインディングに比べて次のような制限があります。
- ビュー バインディングはレイアウト変数またはレイアウト式に対応していないため、動的 UI コンテンツを XML レイアウト ファイルから直接宣言できません。
- ビュー バインディングは双方向データ バインディングに対応していません。
これらの内容をふまえると、プロジェクトでビュー バインディングとデータ バインディングの両方を使用することが最適な場合があります。高度な機能を必要とするレイアウトではデータ バインディングを使用でき、そうでないレイアウトではビュー バインディングを使用できます。
参考情報
ビュー バインディングの詳細については、以下の参考情報をご確認ください。
ブログ
動画
あなたへのおすすめ
- 注: JavaScript がオフになっている場合はリンクテキストが表示されます
- Kotlin 合成から Jetpack ビュー バインディングに移行する
- レイアウトとバインディング式
- アプリ アーキテクチャ: UI レイヤ - スタートガイド - Android デベロッパー