Android DataBinding Tips - Re.Ra.Ku アドベントカレンダー day 10
Re.Ra.Ku アドベントカレンダー 10日目です。
こんにちは、安部です。
今回はAndroidのDataBindingのTipsを少し紹介します。
DataBindingの変数に設定するなど基本的なところは省略しています。
includeしたレイアウトに変数を渡す
include
タグを使ったときに変数を渡す方法です。
includeされるレイアウト
通常のレイアウトと同じようにします。
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="name" type="java.lang.String" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{name}" /> </LinearLayout> </layout>
includeタグ
先程のレイアウトで定義した変数をincludeタグの属性としてapp:name
に渡したい変数を設定することでincludeされた側のほうでも変数が使用可能になります。
<include app:name="@{user.name}" layout="@layout/custom_view"/>
EditTextの入力を変数に反映する
two-way bindingの実現方法です。
入力を受け取るクラス/変数
EditTextの入力を受けて取るクラスと変数の例です。
public class User { public final ObservableField<String> name = new ObservableField<>(); }
レイアウト
分かりにくいのですが、android:text
に設定している値の@
と{
の間に=
を入れます。こうするとEditTextに入力された値が変数に反映されます。
<EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@={user.name}" />
制限事項としては、android:text
にはStringのものしか設定できません。
EditTextの変更を監視する
変更されるたびに呼ばれるメソッド
この場合ですとonTextChanged
が変更されるたびに呼ばれます。
public class Handlers { private static final String TAG = Handlers.class.getSimpleName(); public void onTextChanged(CharSequence s, int start, int before, int count) { Log.d(TAG, "onTextChanged: " + s); } }
レイアウト
android:onTextChanged
に先程つくった処理を設定してあげます。これだけです。
<EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:onTextChanged="@{handlers::onTextChanged}" />
補足
android:onTextChanged
がどこで定義されてるかですが、ココ です。
他にもいくつか拡張であるので、ほしいのがないかをココで探すと良いと思います。
よく使いそうな、フォーカスイベントのandroid:onFocusChange
もココで定義されてます。
ひとつ問題としては、xmlで属性が定義されてないみたいな警告が出てしまいます。気になる人はコメントで抑制しておくと良いと思います。(tools:ignoreを使った抑制がうまくできなかった…)
<!--suppress AndroidUnknownAttribute --> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:onTextChanged="@{handlers::onTextChanged}" />
注意としてはパッとどこで定義されてるのかが分からないので、何かしらコメントがあると良いかもしれないです。
定義されていないイベント処理する
例えば、SwipeRefreshLayout#setOnRefreshListener
はそのままではレイアウトファイルのみでイベントを設定することが出来ません。
カスタム属性を設定する
次のようなクラスを作って、属性を追加して、それを受け取れるようにします。引数の順番は対象のView、属性に渡す型になります。今回はSwipeRefreshLayout.OnRefreshListener
を受け取るようにします。
public class SwipeRefreshLayoutBinding { @BindingAdapter("onRefresh") public static void onRefresh(SwipeRefreshLayout view, SwipeRefreshLayout.OnRefreshListener listener) { view.setOnRefreshListener(listener); } }
別のパターンとして、@BindingMethods
を使うことも出来ます。こちらはクラスのほうにアノテーションを追加します。
@BindingMethods({ @BindingMethod(type = SwipeRefreshLayout.class, attribute = "onRefresh", method = "setOnRefreshListener") }) public class SwipeRefreshLayoutBinding { }
イベントを処理するメソッドを定義する
SwipeRefreshLayout.OnRefreshListener
で定義されているメソッドと同じシグネチャのメソッドを定義します。メソッド名は変更しても大丈夫です。
public class Handlers { private static final String TAG = Handlers.class.getSimpleName(); public void onRefresh() { Log.d(TAG, "onRefresh"); } }
レイアウトに設定する
先程の作った属性(名前空間はappを使います)とメソッドを設定してあげると、コードを書かずにイベントを処理できるようになります。
<android.support.v4.widget.SwipeRefreshLayout android:layout_width="match_parent" android:layout_height="match_parent" app:onRefresh="@{handlers::onRefresh}"> ... </android.support.v4.widget.SwipeRefreshLayout>
まとめ
頑張ればDataBindingで色々できそうですが、あまりトリッキーなことをしすぎない感じのほうが良いとは思います。そこはうまくバランスを取りながらで。
うまくDataBindingを使って、コードを簡潔にしていきたいですね。