web-dev-qa-db-ja.com

スピナーで選択した値をarraylistの値に2方向でバインドする方法

スピナーをArrayListobjectsにバインドすることに成功しました。スピナーから特定のアイテムを選択すると、それが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);
}
11
dzingiskan

さらに簡単な(私の心に)解決策が この質問 で示されています。キーは SpinnerのselectedItemPostion属性 の使用です。これは、質問ですが、リンク先のリポジトリにあります:Android:selectedItemPosition="@={model.position}"

上記のアプローチをうまく使いました。これには、位置から実際のスピナーリストアイテムへのマッピングを行う必要がありますが、とにかくユースケースで行う必要がありました。

8
Maks

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();
    }

}

これを私の実装から簡略化し、テストしませんでした。しかしうまくいくはずです...

7
luca992

Inverse Binding を使用してこれを実現できます。 2つのカスタムバインディングを定義する必要があります。

  1. スピナーから最新の値を取得し、モデルに設定します。
  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}"/>
3
Ajit Singh

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 を参照してください。

0
Arnav Rao