データバインディングのうち、ビューから値を取得する方向のバインディングを独自定義する方法です。
例えば、EditTextの場合はこんな感じで入力されたテキストを取得できます(これは最初から定義済みです)
<!-- EditTextの入力がmodel.textにセットされる -->
<EditText
android:width="wrap_content"
android:height="wrap_content"
android:text="@={model.text}"/>
はじめから定義されていない属性についても、自分でバインディングを書くことで上記のようにして値を取得できます。以下、リストビューのint getCheckedItemPosition()
に対応するバインディングを作成していきます。
完成形のサンプルはこちらにあります。
Step 1: 取得用の属性を定義する
- 適当にカスタムバインディングを定義するクラスを作成する
- クラスに、以下のようなアノテーションを追加
- typeは、対象となるビューのクラス
- attributeは、取得する際のXMLの属性
- methodは、取得に用いるメソッド。省略すると、
get<attribute>
が呼ばれる。- この場合は、
getCheckedItemPosition
が呼ばれる
- この場合は、
- eventは、値の取得タイミング。省略すると、
<attribute>AttrChanged
というイベント時に更新される- 詳しくはStep 2で後述
@InverseBindingMethods({
@InverseBindingMethod(
type = AbsListView.class,
attribute = "checkedItemPosition"
)
})
public class BindingAdapterUtil {
...
}
Step 2: 取得イベントのリスナーを定義する
- データバインディングで値が取得されるタイミングは、Step 1のeventのタイミング
- このevent名で、InverseBindingListenerをセットするバインディングを用意する
public class BindingAdapterUtil {
// STEP 2:
// <属性名>AttrChanged という属性で、InverseBindingListenerをセットするバインディングを定義する。
// InverseBindingListener#onChange()が呼ばれるときに、取得したい値が更新される。
@BindingAdapter(value = "checkedItemPositionAttrChanged")
public static void setAttrChanged(AbsListView absListView, final InverseBindingListener attrChanged) {
if (attrChanged == null) {
absListView.setOnItemClickListener(null);
} else {
absListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
attrChanged.onChange();
}
});
}
}
}
Step 3: 値をビューにセットするバインディングを定義する
- これがないとコンパイルが通らないので。
public class BindingAdapterUtil {
...
// STEP 3:
// 同じ属性名で、値をビューに反映する方向のバインディングも定義しないとコンパイルが通らないので定義する。
@BindingAdapter(value = "checkedItemPosition")
public static void setCheckedItemPosition(AbsListView absListView, int position) {
absListView.setItemChecked(position, true);
}
}
使い方
あとはXMLに書くだけ。
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/textView"
android:choiceMode="singleChoice"
app:checkedItemPosition="@={model.checkedPosition}"/>