Android Spinner two-way binding with @InverseBindingAdapter / AttrChanged doesn't work

android, android-databinding

Solution

You are missing the `=` in the binding expression to notify the system that you would like to use this binding as a two-way binding.

app:selectedCountry="@{customer.country}"

should be

app:selectedCountry="@={customer.country}"

Problem

I've been trying for a while to set up a two-way binding on a Spinner. There are many example of this on the web and here on stack overflow but none of them works for me. It's just a country spinner, I've defined a country adapter for it with this methods: ``` @InverseBindingAdapter(attribute = "selectedCountry", event = "selectedCountryAttrChanged") public static String bindCountryInverseAdapter(AppCompatSpinner pAppCompatSpinner) { Object selectedItem = pAppCompatSpinner.getSelectedItem(); SpinnerAdapter adapter = pAppCompatSpinner.getAdapter(); if (adapter instanceof CountrySpinnerAdapter) { return (String) selectedItem; } throw new UnsupportedOperationException("The adapter must be a CountrySpinnerAdapter"); } @BindingAdapter(value = "selectedCountryAttrChanged", requireAll = false) public static void bindCountryChanged(AppCompatSpinner pAppCompatSpinner, final InverseBindingListener newTextAttrChanged) { AdapterView.OnItemSelectedListener listener = new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { newTextAttrChanged.onChange(); } @Override public void onNothingSelected(AdapterView<?> parent) { newTextAttrChanged.onChange(); } }; pAppCompatSpinner.setOnItemSelectedListener(listener); } @BindingAdapter("selectedCountry") public static void bindCountryValue(AppCompatSpinner pAppCompatSpinner, String newSelectedValue) { SpinnerAdapter adapter = pAppCompatSpinner.getAdapter(); if (adapter instanceof CountrySpinnerAdapter) { ((CountrySpinnerAdapter) adapter).bindSelectedValue(pAppCompatSpinner, newSelectedValue); return; } throw new UnsupportedOperationException("The adapter must be a CountrySpinnerAdapter"); } ``` the `bindCountryChanged` method is never called. I've also tried this variant (following other examples): ``` @BindingAdapter(value = {"selectedCountry", "selectedCountryAttrChanged"}, requireAll = false) public static void bindCountryValueChanged(AppCompatSpinner pAppCompatSpinner, String newSelectedValue, final InverseBindingListener newTextAttrChanged) { AdapterView.OnItemSelectedListener listener = new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { newTextAttrChanged.onChange(); } @Override public void onNothingSelected(AdapterView<?> parent) { newTextAttrChanged.onChange(); } }; pAppCompatSpinner.setOnItemSelectedListener(listener); } ``` This is called but `newTextAttrChanged` is always null. The layout, binding part: ``` <data> <variable name="customer" type="my.package.CustomerBinding" /> </data> ``` And the widget: ``` <android.support.v7.widget.AppCompatSpinner android:id="@+id/editCountry" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:spinnerMode="dropdown" app:adapter="@{customer.countrySpinnerAdapter}" app:selectedCountry="@{customer.country}" /> ``` `country` id just an `ObservableField<String>` and `countrySpinnerAdapter` is a `BaseAdapter` for the list of countries. Android gradle plugin: ``` classpath 'com.android.tools.build:gradle:2.2.1' ``` Tools versions: ``` buildToolsVersion "24.0.2" ``` And of course data binding is enabled: ``` dataBinding { enabled = true } ``` Why `newTextAttrChanged` is always null / the BindingAdapter is never calld? What am I doing wrong?

Original source