レイアウトとバインディング式

式言語を使用すると、ディスパッチされたイベントを処理する式を記述できます。 視聴回数で表します。データ バインディング ライブラリは、必要なクラスを を使用して、レイアウト内のビューをデータ オブジェクトをバインドします。

データ バインディングのレイアウト ファイルは若干異なり、次のルートタグで始まります。 layout の後に data 要素と view ルート要素が続きます。このビュー 要素は、バインディングのないレイアウト ファイル内のルート要素です。次のコードでは、 に、サンプルのレイアウト ファイルを示します。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"/>
   </LinearLayout>
</layout>

data 内の user 変数は、内部で使用できるプロパティを表します。 このレイアウトは

<variable name="user" type="com.example.User" />

レイアウト内の式は、 @{} 構文。次の例では、 TextView テキストは user 変数の firstName プロパティ:

<TextView android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@{user.firstName}" />

データ オブジェクト

User エンティティを記述するプレーン オブジェクトがあるとします。

Kotlin

data class User(val firstName: String, val lastName: String)

Java


public class User {
  public final String firstName;
  public final String lastName;
  public User(String firstName, String lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
  }
}

この型のオブジェクトには、変更されることがないデータが格納されます。一般的に、アプリでは 一度読み取られ、その後変更されないデータですまた、kubectl の たとえば、 Java プログラミング言語で利用できます。

Kotlin

// Not applicable in Kotlin.
data class User(val firstName: String, val lastName: String)

Java

public class User {
  private final String firstName;
  private final String lastName;
  public User(String firstName, String lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
  }
  public String getFirstName() {
      return this.firstName;
  }
  public String getLastName() {
      return this.lastName;
  }
}

データ バインディングの観点からすると、これら 2 つのクラスは同等です。「 次に使用される式 @{user.firstName} android:text 属性は、前のクラスの firstName フィールドにアクセスし、 getFirstName() メソッドを使用します。また、 firstName()(メソッドが存在する場合)。

データのバインド

バインディング クラスはレイアウト ファイルごとに生成されます。デフォルトでは、 クラスは、パスカルケースに変換されたレイアウト ファイルの名前に基づいており、 Binding サフィックスが追加されます。たとえば、上のレイアウト ファイル名は、 activity_main.xml。対応する生成されるバインディング クラス: ActivityMainBinding

このクラスは、レイアウト プロパティのすべてのバインディングを保持します。たとえば、 user 変数(レイアウトのビューに割り当て、値の割り当て方法がわかっているもの) バインディング式を指定しますインフレート中にバインディングを作成することをおすすめします。 配置する必要があります。

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val binding: ActivityMainBinding = DataBindingUtil.setContentView(
            this, R.layout.activity_main)

    binding.user = User("Test", "User")
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
   User user = new User("Test", "User");
   binding.setUser(user);
}

実行時に、アプリの UI に Test ユーザーが表示されます。別の方法として、 使用してビューを取得 LayoutInflater: 次の例をご覧ください。

Kotlin

val binding: ActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater())

Java

ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());

データ バインディング アイテムを Fragment ListView、または RecyclerView 使用する場合は、 inflate() バインディング クラスのメソッド、または DataBindingUtil クラスを 次のコードサンプルをご覧ください。

Kotlin

val listItemBinding = ListItemBinding.inflate(layoutInflater, viewGroup, false)
// or
val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)

Java

ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
// or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

式言語

一般的な機能

式言語はマネージコードの式によく似ています。マイページ 式言語で次の演算子とキーワードを使用できます。

  • 数学: + - / * %
  • 文字列の連結: +
  • 論理: && ||
  • バイナリ: & | ^
  • 単項: + - ! ~
  • シフト: >> >>> <<
  • 比較: == > < >= <=<&lt; としてエスケープする必要があります)
  • instanceof
  • グループ: ()
  • リテラル(文字、文字列、数値、null など)
  • キャスト
  • メソッド呼び出し
  • フィールド アクセス
  • 配列アクセス: []
  • 3 項演算子: ?:

次に例を示します。

android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'

使用できない演算

使用可能な式の構文に、次の演算がありません マネージド・コードで:

  • this
  • super
  • new
  • 明示的な汎用呼び出し

null 合体演算子

null 合体演算子(??)が null でない場合、左のオペランドを選択します 前者が null の場合は右になります。

android:text="@{user.displayName ?? user.lastName}"

これは機能的に以下と同等です。

android:text="@{user.displayName != null ? user.displayName : user.lastName}"

プロパティ参照

式は、次の形式を使用してクラス内のプロパティを参照できます。 これはフィールド、ゲッター、 ObservableField オブジェクト:

android:text="@{user.lastName}"

null ポインタ例外を回避する

生成されたデータ バインディング コードは null 値を自動的にチェックし、 null ポインタ例外。たとえば、式 @{user.name} で、 user が null の場合、user.name にはデフォルト値の null が割り当てられます。もし 参照 user.age(age が int 型)の場合、データ バインディングでは 0 に設定されます。

ビューの参照

式は、以下を使用して ID でレイアウト内の他のビューを参照できます。 構文:

android:text="@{exampleText.text}"

次の例では、TextView ビューが次の EditText ビューを参照しています。 行います。

<EditText
    android:id="@+id/example_text"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"/>
<TextView
    android:id="@+id/example_output"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{exampleText.text}"/>

コレクション

配列、リスト、スパース リスト、 便宜上 [] 演算子を使用します。

<data>
    <import type="android.util.SparseArray"/>
    <import type="java.util.Map"/>
    <import type="java.util.List"/>
    <variable name="list" type="List&lt;String>"/>
    <variable name="sparse" type="SparseArray&lt;String>"/>
    <variable name="map" type="Map&lt;String, String>"/>
    <variable name="index" type="int"/>
    <variable name="key" type="String"/>
</data>
...
android:text="@{list[index]}"
...
android:text="@{sparse[index]}"
...
android:text="@{map[key]}"

object.key 表記を使用して、マップの値を参照することもできます。対象 たとえば、上記の例の @{map[key]}@{map.key}

文字列リテラル

属性値を単一引用符で囲むと、 二重引用符で囲む必要があります。

android:text='@{map["firstName"]}'

二重引用符で属性値を囲むこともできます。その際、 次に示すように、文字列リテラルはバッククォート ` で囲む必要があります。 こちらをご覧ください。

android:text="@{map[`firstName`]}"

リソース

次の構文により、式でアプリのリソースを参照できます。

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

パラメータを指定することにより、書式設定文字列と複数形を評価できます。

android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"

プロパティ参照ビュー リソース パラメータとして渡す参照:

android:text="@{@string/example_resource(user.lastName, exampleText.text)}"

複数形が複数のパラメータを取る場合は、すべてのパラメータを渡します。


  Have an orange
  Have %d oranges

android:text="@{@plurals/orange(orangeCount, orangeCount)}"

一部のリソースでは、次に示すように、明示的な型の評価が必要です。 table:

タイプ 通常の参照 式参照
String[] @array @stringArray
int[] @array @intArray
TypedArray @array @typedArray
Animator @animator @animator
StateListAnimator @animator @stateListAnimator
color int @color @color
ColorStateList @color @colorStateList

イベント処理

データ バインディングを使用すると、イベントを処理する式を記述して、 ビュー(たとえば、 onClick() メソッドを呼び出します。イベント属性名は、リスナー メソッドの名前によって決定されます。 いくつか例外がありますたとえば View.OnClickListener の例: メソッド onClick() なので、このイベントの属性は android:onClick です。

クリック イベント専用のイベント ハンドラがいくつかあり、 android:onClick 以外の属性を指定することをおすすめします。こちらの 次の属性を追加して、この種の競合を回避してください。

クラス リスナーのセッター 属性
SearchView setOnSearchClickListener(View.OnClickListener) android:onSearchClick
ZoomControls setOnZoomInClickListener(View.OnClickListener) android:onZoomIn
ZoomControls setOnZoomOutClickListener(View.OnClickListener) android:onZoomOut

この 2 つのメカニズムを使用できます。詳細については、 イベントを処理します。

  • メソッド参照: 式では、 リスナー メソッドのシグネチャに適合する参照メソッドが含まれます。日時 式はメソッド参照として評価され、データ バインディングによってメソッドがラップされる オーナー オブジェクトを参照し、そのリスナーを ターゲット ビューです。式が null と評価された場合、データ バインディングは実行されません。 代わりにリスナーを作成し、null リスナーを設定してください。
  • リスナー バインディング: リスナー バインディング イベント発生時に評価されますデータ バインディングでは常に、 ビューに設定します。イベントがディスパッチされると、 リスナーはラムダ式を評価します。

メソッド参照

他の処理と同様に、イベントをハンドラ メソッドに直接バインドできます。 割り当て android:onClick を メソッドを呼び出せますGoogle Workspace の View onClick 属性は、 コンパイル時に処理されます。そのため、このメソッドが存在しない場合や、 コンパイル時エラーが発生します。

メソッド参照とリスナー バインディングの主な違いは、 実際のリスナーの実装は、データがバインドされた時点で作成され、 イベントがトリガーされます。イベントが発生したときに式を評価したい場合は、 リスナー バインディングを使用してください。

イベントをそのハンドラに割り当てるには、通常のバインディング式を使用して、 value は呼び出すメソッド名です。たとえば、次の例をご覧ください。 レイアウト データ オブジェクト:

Kotlin

class MyHandlers {
    fun onClickFriend(view: View) { ... }
}

Java

public class MyHandlers {
    public void onClickFriend(View view) { ... }
}

バインディング式では、ビューのクリック リスナーを onClickFriend() メソッドを次のように指定します。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="handlers" type="com.example.MyHandlers"/>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"
           android:onClick="@{handlers::onClickFriend}"/>
   </LinearLayout>
</layout>

リスナー バインディング

リスナー バインディングは、イベントの発生時に実行されるバインディング式です。。 メソッド参照に似ていますが、任意のデータ バインディングを実行できる 表します。この機能は Gradle 用 Android Gradle プラグインで使用できます 使用できます。

メソッド参照では、メソッドのパラメータは、 イベントリスナー。リスナー バインディングでは、戻り値のみが リスナーの期待される戻り値(void が想定されている場合を除く)。対象 たとえば、onSaveClick() がある次のプレゼンター クラスについて考えてみましょう。 メソッド:

Kotlin

class Presenter {
    fun onSaveClick(task: Task){}
}

Java

public class Presenter {
    public void onSaveClick(Task task){}
}

次のように、クリック イベントを onSaveClick() メソッドにバインドできます。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="task" type="com.android.example.Task" />
        <variable name="presenter" type="com.android.example.Presenter" />
    </data>
    <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent">
        <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:onClick="@{() -> presenter.onSaveClick(task)}" />
    </LinearLayout>
</layout>

式でコールバックを使用すると、データ バインディングによって自動的に それをイベントに登録します。ビューが イベントが発生すると、データ バインディングは指定された式を評価します。通常のバインディングと同じ null とスレッドの安全性を確保しながら、データ バインディングを 評価しています。

上記の例では、onClick(View) に渡される view パラメータは、 定義します。リスナー バインディングでは、リスナー パラメータに次の 2 つの選択肢があります。 メソッドのすべてのパラメータを無視することも、すべてのパラメータに名前を付けることもできます。ご希望の場合 パラメータに名前を付ける場合は、式で使用できます。たとえば、 上の式は次のように記述できます。

android:onClick="@{(view) -> presenter.onSaveClick(task)}"

パラメータを式で使用する場合は、次の操作を行います。

Kotlin

class Presenter {
    fun onSaveClick(view: View, task: Task){}
}

Java

public class Presenter {
    public void onSaveClick(View view, Task task){}
}

android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"

また、ラムダ式と複数のパラメータを使用できます。

Kotlin

class Presenter {
    fun onCompletedChanged(task: Task, completed: Boolean){}
}

Java

public class Presenter {
    public void onCompletedChanged(Task task, boolean completed){}
}

<CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
      android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />

リッスンしているイベントが void 以外の型の値を返す場合、 式も同じ型の値を返す必要があります。たとえば 感知して長押し(長押し)イベントがある場合、式は ブール値を返します。

Kotlin

class Presenter {
    fun onLongClick(view: View, task: Task): Boolean { }
}

Java

public class Presenter {
    public boolean onLongClick(View view, Task task) { }
}

android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"

null オブジェクトが原因で式を評価できない場合、データ バインディングは その型のデフォルト値(参照型の null0 など)。 int、または boolean の場合は false

述語を含む式を使用する必要がある場合(例: 3 項表記—void を記号として使用できます。

android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"

複雑なリスナーを使用しない

リスナー式は強力で、コードを読みやすくします。 一方、リスナーに複雑な式が含まれていると、レイアウトが難しくなります 管理する必要がありません。利用可能なデータを渡して式をシンプルに保つ コールバックメソッドに渡します内部にビジネス ロジックを実装する。 コールバック関数を呼び出します。

インポート、変数、インクルード

データ バインディング ライブラリには、インポート、変数、 含まれます。インポートにより、レイアウト ファイル内でクラスを参照しやすくなります。 変数を使用すると、バインディング式で使用できるプロパティを記述できます。 インクルードを使用すると、複雑なレイアウトをアプリ全体で再利用できます。

インポート

インポートを使用すると、マネージド コードと同様に、レイアウト ファイル内でクラスを参照できます。 data 要素内では、0 個以上の import 要素を使用できます。「 次のコード例では、View クラスをレイアウト ファイルにインポートしています。

<data>
    <import type="android.view.View"/>
</data>

View クラスをインポートすると、バインディング式から参照できます。 次の例は、Terraform の VISIBLEView クラスの GONE 定数:

<TextView
   android:text="@{user.lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

型のエイリアス

クラス名が競合する場合は、いずれかのクラスの名前を 使用します。次の例では、View クラスの名前を com.example.real.estate パッケージを Vista に追加します。

<import type="android.view.View"/>
<import type="com.example.real.estate.View"
        alias="Vista"/>

その後、Vista を使用して com.example.real.estate.ViewView を参照できます。 レイアウト ファイル内で android.view.View を参照しています。

他のクラスをインポートする

インポートした型は、変数と式での型参照として使用できます。「 次の例では、変数の型として UserList が使用されています。

<data>
    <import type="com.example.User"/>
    <import type="java.util.List"/>
    <variable name="user" type="User"/>
    <variable name="userList" type="List&lt;User>"/>
</data>

インポートされた型を使用して、式の一部をキャストできます。次の この例では、connection プロパティを User の型にキャストしています。

<TextView
   android:text="@{((User)(user.connection)).lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

また、インポートされた型は、 表します。次のコードは、MyStringUtils クラスをインポートして参照します。 capitalize メソッドを使用します。

<data>
    <import type="com.example.MyStringUtils"/>
    <variable name="user" type="com.example.User"/>
</data>
…
<TextView
   android:text="@{MyStringUtils.capitalize(user.lastName)}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

マネージコードと同様に、java.lang.* は自動的にインポートされます。

変数

data 要素内で複数の variable 要素を使用できます。各 variable 要素は、使用するレイアウトに設定できるプロパティを記述します。 レイアウト ファイル内のバインディング式で使用できます。次の例では、 userimagenote 変数:

<data>
    <import type="android.graphics.drawable.Drawable"/>
    <variable name="user" type="com.example.User"/>
    <variable name="image" type="Drawable"/>
    <variable name="note" type="String"/>
</data>

変数の型はコンパイル時に検査されるため、変数に Observable または 監視可能なコレクション 型に反映する必要があります。変数が基本クラスまたはインターフェースの場合 Observable インターフェースを実装していないアプリの場合、変数は 表示されます。

さまざまな構成(たとえば、 横向き、縦向きなど)に応じて、変数が組み合わされます。重複しない 変数定義が競合する場合があるからです。

生成されるバインディング クラスには、記述された各要素のセッターとゲッターが 使用します。セッターまで、変数はマネージド コードのデフォルト値を取ります。 が呼び出されます。null は参照タイプ、0intfalseboolean など

バインディング式で使用するために context という名前の特殊な変数が生成されます。 できます。context の値は、 Context オブジェクト(ルートビューの getContext() メソッドを使用します。「 context 変数は、明示的な変数宣言によってオーバーライドされます。 表示されます。

インクルード

含まれるレイアウトのバインディングに、変数を渡すことができます。 属性でアプリの名前空間と変数名を使用して作成します。「 次の例では、name.xml からインクルードされた user 変数と、 contact.xml レイアウト ファイル:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:bind="http://schemas.android.com/apk/res-auto">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <include layout="@layout/name"
           bind:user="@{user}"/>
       <include layout="@layout/contact"
           bind:user="@{user}"/>
   </LinearLayout>
</layout>

データ バインディングでは、merge 要素の直接の子としての include はサポートされていません。 たとえば、次のレイアウトはサポートされていません。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:bind="http://schemas.android.com/apk/res-auto">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <merge><!-- Doesn't work -->
       <include layout="@layout/name"
           bind:user="@{user}"/>
       <include layout="@layout/contact"
           bind:user="@{user}"/>
   </merge>
</layout>

参考情報

データ バインディングについて詳しくは、以下の参考リンクをご覧ください。

サンプル

Codelab

ブログ投稿