Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to 2 way bind spinner selected value to value in arraylist

I managed to bind spinner to ArrayList of objects, now I need to make it that when you select certain item from spinner that it reflects to ViewModel (setter gets called and sets value of a variable to what selected index is in spinner)

I managed to make it work the other way around, value from viewmodel is reflected to view ( like this How to use DataBindingUtil with an Android spinner? ).

relevant 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>

custom binding

@BindingAdapter("bind:selection")
public static void bindSelection(Spinner spinner, int position) {
    spinner.setSelection(position);
}
like image 766
dzingiskan Avatar asked May 18 '16 08:05

dzingiskan


2 Answers

Pretty much the same as Ajit's answer but using android:entries instead of binding an adapter.

                    <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}">

Then my observable class

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

}

Simplified this from my implementation and didn't test it. Should work though...

like image 50
luca992 Avatar answered Oct 01 '22 15:10

luca992


An even simpler (to my mind) solution is presented in this question, the key being the use of the selectedItemPostion attribute of the Spinner which is not in the code included with the question but is in the repo that it links to: android:selectedItemPosition="@={model.position}"

I used the above approach successfully. This does require doing the mapping from position to actual spinner list items but I needed to do that for my use case anyway.

like image 37
Maks Avatar answered Oct 01 '22 16:10

Maks