Layout XMLでできること

Data BindingにはLayout XMLでの記述とコード上での記述という2つの側面があります。本節ではLayout XML側でできることを掘り下げていきます。

import要素

前節ではlayout、data、variableの3つの要素を使いましたが、もう一つimportという要素が利用できます。import要素はJavaでのimport文と同じです。リスト8のように定義しておくと、簡潔にクラスを参照できます。変数の宣言のときに利用するだけではメリットはあまり感じられませんが、後述する演算子やメソッド呼び出しの利用の際に効果を発揮します。

リスト2.8 import要素を定義する

<data>
  <import type="com.sys1yagi.sample.entities.Article"/>
  <variable name="article" type="Article"/>
</data>

また、alias属性を使ってクラスに別名を付けることもできます。リスト9のようにパッケージは異なるがクラス名が同じといった場合に利用します。

リスト2.9 クラス名が同じ場合aliasで別名を付ける

<data>
  <import type="com.sys1yagi.sample.entities.Article"/>
  <import type="com.sys1yagi.sample.api.models.Article"
    alias="ArticleModel" />
  <variable name="article" type="Article"/>
  <variable name="articleModel" type="ArticleModel"/>
</data>

Bindingクラスに任意の名前を付ける

Bindingクラスの名前は次の規則に沿って決まります。

  1. パッケージ名は"アプリケーションID.databinding"
  2. Layout XMLのファイル名をキャメルケースにする
  3. suffixに"Binding"を付ける

例えばアプリケーションIDがcom.sys1yagi.androidでLayout XMLのファイル名がfragment_main.xmlだった場合次のクラスを生成します。

com.sys1yagi.android.databinding.FragmentMainBinding

ほとんどの場合このデフォルトの命名規則で不都合はないと思いますが、Layout XMLのdata要素でclass属性を指定すると生成するBindingクラスの名前やパッケージ名を変更できます。

リスト2.10 class属性にクラス名を指定する

<data class="MainBinding">
</data>

リスト10のようにclass属性にクラス名だけを指定した場合、"アプリケーションID.databinding"パッケージに指定した名前のクラスが生成されます。

<data class=".binding.MainBinding">
</data>

先頭に"."を付与するとアプリケーションID配下のパッケージを指定できます。これはAndroidManifest.xmlでacitivtyを定義するときに利用できる方法と同じです。

<data class="com.sys1yagi.binding.MainBinding">
</data>

また、FQDNで指定することもできます。Bindingクラスの名前はレイアウトファイルの名前から自動的に決まってしまうので、名前が長すぎたりわかりにくい場合に利用するとよいでしょう。

includeを使う

Data Bindingを利用するレイアウトをinclude要素を使って構造化できます。include要素で利用するためのレイアウトを作って試してみましょう。

リスト2.11 view_article_url.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
  <data>
    <variable name="article" type="com.sys1yagi.sample.entities.Article"/>
  </data>

  <LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <TextView
      android:text='@{article.url}'
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      tools:text="url"
      />
  </LinearLayout>
</layout>

リスト11で定義したview_article_url.xmlをactivity_main.xmlで使ってみましょう。

リスト2.12 view_article_url.xmlをincludeする

<layout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:bind="http://schemas.android.com/apk/res-auto">

  <data>
    <variable name="article" type="com.sys1yagi.sample.entities.Article"/>
  </data>

  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical">
    <TextView  android:text='@{article.title}'
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"/>
    <include
      layout="@layout/view_article_url"
      bind:article="@{article}"
      />
  </LinearLayout>
</layout>

includeするレイアウトがData Bindingを利用している場合、カスタム属性をつかって対象のレイアウトへデータを渡すことができます。リスト12ではカスタム属性の名前空間を"bind"とし、"bind:article"属性を使ってArticleを渡しています。属性名はinclude元で定義しているvariable要素のname属性と一致している必要があります。

式を利用する

Data Binding LibraryはLayout XML上での式の利用をサポートしています。表2に式で利用できる演算子や処理の一覧を示します。

表2.2: Data Binding Libraryで利用できる演算子

演算子
四則演算 + - / * %
文字列連結 +
論理演算 &&
ビット演算 & ^
単項演算 + - ! ~
シフト >> >>> <<
比較 == > < >= <=
instanceof なし
グループ化 ()
リテラル(character、String、numeric、null) なし
キャスト なし
メソッド呼び出し なし
フィールドへのアクセス なし
配列へのアクセス []
三項演算子 ?:

式の利用の例として、"article.titleが空かnullだったらno titleという文言に置き換えたい"という条件を考えてみます。リスト13はandroid.text.TextUtilsクラスを使ってarticle.titleの空チェックをし、三項演算子によってセットする値を制御する例です。このように状態に合わせた制御をLayout XMLに記述できます。式で利用するクラスはimport要素によって宣言するかFQDNで記述しないと参照できないので注意しましょう。

リスト2.13 演算子を使ってデフォルト値を設定する

<data>
  <import type="com.sys1yagi.sample.entities.Article"/>
  <import type="android.text.TextUtils"/>
  <variable name="article" type="Article"/>
</data>

<TextView
  android:text='@{TextUtils.isEmpty(article.title) ? "empty" : article.title}'
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"/>

Null合体演算子

Layout XMLに記述する式にはNull合体演算子という演算子が利用できます。これはData Bidning Library専用の演算子です。Null合体演算子はnullチェックによる三項演算子の省略記法を提供します。次のように左辺がnullでなければ左辺値、nullだったら右辺値を返却します。

android:text="@{article.title ?? String.valueOf(article.id)}"

式や表記の制約

式の利用は概ねJavaと同様のことができますが、一部に制約もあります。Layout XML上では次の予約語等が利用ができません。

  • this
  • super
  • new
  • 明示的なジェネリクスの呼び出し

また、一部でXMLの制約を受けることに注意しなければなりません。例えば型パラメータを記述する為に"<>"を使いますが、その内"<"の方は"<"にエスケープしなければなりません(リスト14)。

リスト2.14 "<"をエスケープする

<data>
  <variable name="adapter" type="android.widget.ArrayAdapter&lt;Article>"/>
</data>

このほか式の論理演算などで"&"を利用する場合"&"と記述する必要があります(リスト15)。

リスト2.15 "&"をエスケープする

android:visibility="@{!error &amp;&amp; !progress ? View.VISIBLE : View.GONE}"

文字列の取り扱いも少し特殊です。書き方にバリエーションがあります(リスト16)。

リスト2.16 文字列の書き方

// シングルクォートで全体を囲む
android:text='@{"Name" + name}'
// 文字列部分をバッククォートで囲む
android:text="@{`Name` + name}"
// ダブルクォートをエスケープする
android:text="@{&quot;Name&quot; + name}"

条件に合わせてリソースを変更する

プロパティの値や定数だけでなく、リソースも式の返却値にできます。リスト17は条件に応じてdimenを変更する例です。

リスト2.17 @dimenを状態に合わせて変更する

android:padding="@{large ? (int)@dimen/large_margin : (int)@dimen/small_margin}"

リスト17では返却値をintにキャストをしています。なぜキャストが必要なのか生成されたBindingクラスの実装を探ってみましょう。リスト18はBindingクラスの@dimen/large_marginを取り出す部分の抜粋です。

リスト2.18 リソースはこのようなコードに変換される

(int) getRoot().getResources().getDimension(R.dimen.large_margin)

getDimension()メソッドはfloatを返す一方でandroid:padding属性へのバインドはsetPadding(int)メソッドを使って行うためそのままではエラーとなってしまいます。このためリスト17ではintへキャストをしています。コードに変換したとき、属性値と返却値がどのような型になるか留意する必要があります。

引数を持つリソース

stringリソースは単純な参照だけでなく、引数を取ってformatをするFormattingAndStyling場合があります(リスト19)。

リスト2.19 引数を持つstringリソース

<string name="title">Title:%1$s</string>

Layout XMLではメソッド呼び出しのように記述できます(リスト20)。

リスト2.20 メソッドのように呼び出す

<TextView
  android:text='@{@string/title(article.title)}'
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"/>

pluralsリソースついても同様です。リスト21のようなpluralsリソースを定義したとします。

リスト2.21 formatArgsを持つstringリソースやpluralsリソースを使う

<plurals name="article_count">
  <item quantity="zero">Not Found</item>
  <item quantity="other">Result : %1$d</item>
</plurals>

リスト22のように呼び出します。第一引数がquantity、第二引数以降がformatArgsとなります。

リスト2.22 formatArgsを持つstringリソースやpluralsリソースを使う

<TextView
  android:text='@{@plurals/article_count(count, count)}'
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"/>
FormattingAndStyling. http://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling