ビュー バインディング 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 ファイルの名前をパスカルケースに変換し、末尾に「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
という名前になります。このクラスには、name
という名前の TextView
と button
という Button
という 2 つのフィールドがあります。レイアウト内の ImageView
には ID がないため、バインディング クラス内でその 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
型のフィールド userBio
を公開することが想定されます。これは、TextView
が共通の基本クラスであるためです。技術的な制限により、ビュー バインディングのコード生成ツールはこれを特定できず、代わりに 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 ファイルで参照するビューと一致する型があります。つまり、クラスキャスト例外のリスクはありません。
レイアウトとコードの間に互換性がないと、実行時ではなくコンパイル時にビルドが失敗することになります。
データ バインディングとの比較
ビュー バインディングとデータ バインディングはどちらも、ビューを直接参照するために使用できるバインディング クラスを生成します。ただし、ビュー バインディングはより単純なユースケースを扱うことを想定しており、データ バインディングと比べて次のようなメリットがあります。
- 高速なコンパイル: ビュー バインディングはアノテーション処理を必要としないため、コンパイル時間が短縮されます。
- 使いやすさ: ビュー バインディングでは特別なタグが付いた XML レイアウト ファイルが必要ないため、アプリにすばやく導入できます。モジュールでビュー バインディングを有効にすると、そのモジュールのすべてのレイアウトに自動的に適用されます。
一方、ビュー バインディングには、データ バインディングに比べて次のような制限があります。
- ビュー バインディングはレイアウト変数やレイアウト式をサポートしていないため、XML レイアウト ファイルから直接動的 UI コンテンツを宣言するために使用することはできません。
- ビュー バインディングは、双方向データ バインディングをサポートしていません。
以上のことから、1 つのプロジェクトでビュー バインディングとデータ バインディングの両方を使用するほうがよい場合もあります。高度な機能が必要なレイアウトではデータ バインディングを、必要のないレイアウトではビュー バインディングを使用できます。
参考情報
ビュー バインディングの詳細については、以下の参考情報をご覧ください。
サンプル
ブログ
動画
あなたへのおすすめ
- 注: JavaScript がオフになっている場合はリンクテキストが表示されます
- Kotlin 合成から Jetpack ビュー バインディングに移行する
- レイアウトとバインディング式
- アプリ アーキテクチャ: UI レイヤ - スタートガイド - Android デベロッパー