スピナーをArrayList
のobjects
にバインドすることに成功しました。スピナーから特定のアイテムを選択すると、それがViewModel
に反映されるようにする必要があります(setterが呼び出され、変数の値をスピナーで選択されているインデックスの値に設定します)
私はそれを逆に機能させることができました、viewmodel
の値はview
に反映されます(このように DataBindingUtilをAndroidスピナー?)。
関連するxml
<data>
<variable
name="spinnerList"
type="com.example.root.proj.viewmodels.MonumentTypeVMList"/>
</data>
<Spinner
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
bind:spinnerbind="@{spinnerList.list}"
bind:selection="@{spinnerList.selection}"
></Spinner>
カスタムバインディング
@BindingAdapter("bind:selection")
public static void bindSelection(Spinner spinner, int position) {
spinner.setSelection(position);
}
さらに簡単な(私の心に)解決策が この質問 で示されています。キーは SpinnerのselectedItemPostion属性 の使用です。これは、質問ですが、リンク先のリポジトリにあります:Android:selectedItemPosition="@={model.position}"
上記のアプローチをうまく使いました。これには、位置から実際のスピナーリストアイテムへのマッピングを行う必要がありますが、とにかくユースケースで行う必要がありました。
Ajitの回答とほとんど同じですが、アダプターをバインドする代わりにAndroid:entriesを使用します。
<Android.support.v7.widget.AppCompatSpinner
Android:id="@+id/typeSpinner"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:entries="@{vehicle.types}"
Android:selectedItemPosition="@={vehicle.selectedTypePosition}">
その後、私の観察可能なクラス
@InverseBindingMethods({
@InverseBindingMethod(type = AppCompatSpinner.class, attribute = "Android:selectedItemPosition"),
})
class Vehicle extends BaseObservable {
String[] types = getResources().getStringArray(R.array.service_types);
String type = null;
@Bindable
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
notifyPropertyChanged(BR.type);
}
Integer selectedTypePosition = 0;
@Bindable
public Integer getSelectedTypePosition() {
return selectedTypePosition;
}
public void setSelectedTypePosition(Integer selectedTypePosition) {
this.selectedTypePosition = selectedTypePosition;
type = types[selectedTypePosition];
}
@BindingAdapter("selectedItemPositionAttrChanged")
void setSelectedItemPositionListener(AppCompatSpinner view, final InverseBindingListener selectedItemPositionChange) {
if (selectedItemPositionChange == null) {
view.setOnItemSelectedListener(null);
} else {
view.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
selectedItemPositionChange.onChange();
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
}
@InverseBindingAdapter(attribute = "selectedItemPosition")
Integer getSelectedItemPosition(AppCompatSpinner spinner) {
return spinner.getSelectedItemPosition();
}
}
これを私の実装から簡略化し、テストしませんでした。しかしうまくいくはずです...
Inverse Binding を使用してこれを実現できます。 2つのカスタムバインディングを定義する必要があります。
スピナーから選択した値を取得するには、このメソッドをカスタムバインディングに追加します。
@InverseBindingAdapter(attribute = "selection", event = "selectionAttrChanged")
public static String getSelectedValue(AdapterView view) {
return (String) view.getSelectedItem();
}
スピナーで新しい値が選択されたときにイベントをトリガーするには
@BindingAdapter(value = {"selection", "selectionAttrChanged", "adapter"}, requireAll = false)
public static void setAdapter(AdapterView view, String newSelection, final InverseBindingListener bindingListener, ArrayAdapter adapter) {
view.setAdapter(adapter);
view.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
bindingListener.onChange();
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
//Nothing
}
});
if (newSelection != null) {
int pos = ((ArrayAdapter) view.getAdapter()).getPosition(newSelection);
view.setSelection(pos);
}
}
レイアウトファイルで、上記のバインディングを次のように使用します。
<Spinner
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
app:adapter="@{AdapterFactory.getAdapter(context)}"
app:selection="@={model.selectedValue}"/>
Spinnerの双方向バインディングを実装するには
1)カスタム属性を使用し、双方向バインディング式を追加して、データモデルのプロパティをカスタム属性にバインドします。以下のコードでは、bind:paymentModeはカスタム属性です。
<Android.support.v7.widget.AppCompatSpinner
. . .
bind:pmtOpt="@={accountSettings.defaultPaymentOption}"
app:adapter="@{spinAdapter}" />
2)次に、この属性に対してデータバインディング(データモデルからスピナーへ)が発生したときに使用する必要がある動作またはセッターメソッドを提供するには、BindingAdapterを定義する必要があります。バインディングが発生したとき、デフォルトの選択値を設定し、項目選択リスナーをスピナーに追加する必要があります。リスナーでは、項目選択イベントが発生したときに、InverseBindingListenerを使用して値をキャプチャするようにデータバインディングフレームワークに通知し、逆バインディングを開始する必要があります。
@BindingAdapter(value = {"bind:paymentMode",
"bind:paymentModeAttrChanged"}, requireAll = false)
public static void setPaymentMode(final AppCompatSpinner spinner,
final String selectedPmtMode,
final InverseBindingListener changeListener) {
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
changeListener.onChange();
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
changeListener.onChange();
}
});
spinner.setSelection(getIndexOfItem(spinner, selectedPmtMode));
}
3)フレームワークがユーザー選択値を取得してデータモデルに設定するために使用する逆バインディングアダプター(スピナーからデータモデル)を定義します。これは、InverseBindingListenerが通知を受け取ったときに呼び出されます。
@InverseBindingAdapter(attribute = "bind:paymentMode",
event = "bind:paymentModeAttrChanged")
public static String getPaymentMode(final AppCompatSpinner spinner) {
return (String)spinner.getSelectedItem();
}
データバインディングとスピナーの双方向バインディング実装の詳細については、 http://www.zoftino.com/Android-data-binding-library-tutorial を参照してください。