18

I need to understand how the Data Binding Library determine the order of execution for its BindingAdapters. If I have two BindingAdapters for a View and if the View has both the attributes corresponding to those BindingAdapters, how will it determine which adapter will be executed first? I ask because the order of execution matters in my case.

I have the following BindingAdapter/s:

public class SpinnerBindingAdapter {

    @BindingAdapter(value = {"entries"})
    public static void setEntries(Spinner spinner, List<? extends SpinnerItem> spinnerItems) {

        for (int i = 0; i < spinnerItems.size(); i++) {
            spinnerItems.get(i).setIndex(i);
        }
        ArrayAdapter<? extends SpinnerItem> adapter =
                new ArrayAdapter<>(spinner.getContext(), R.layout.spinner_item, spinnerItems);
        spinner.setAdapter(adapter);
    }

    @InverseBindingAdapter(attribute = "selectedItem", event = "selectedItemAttrChanged")
    public static Object getSelectedItem(Spinner spinner) {

        Object selectedItem = spinner.getSelectedItem();

        return selectedItem;
    }

    @BindingAdapter(value = {"selectedItem"})
    public static void setSelectedItem(Spinner spinner, SpinnerItem spinnerItem) {
        if (spinner.getAdapter() == null) {
            return;
        }
        // Other code omitted for simplicity
    }

    @BindingAdapter(value = {"selectedItemAttrChanged"}, requireAll = false)
    public static void setOnItemSelectedListener(Spinner spinner, final InverseBindingListener selectedItemChange) {
        if (selectedItemChange == null) {
            spinner.setOnItemSelectedListener(null);
        } else {
            spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

                @Override
                public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                    selectedItemChange.onChange();
                }

                @Override
                public void onNothingSelected(AdapterView<?> parent) {

                }
            });
        }
    }
}

And here is how I populate the Spinner and set the selection:

<Spinner
    android:id="@+id/spinner_system_activity_edit_tracker_unit"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="fill_horizontal"
    app:entries="@{DatabaseModel.queryForAll()}"
    app:selectedItem="@={object.selectedItem}"/>

DatabaseModel.queryForAll is a static method that queries the database and returns a list of objects which is then given to the BindingAdapter. The BindingAdapter takes this list, update each of its item with an index and set it as an adapter for the spinner.

For whatever reason, the "setSelectedItem" BindingAdapter always gets called first. This is undesirable, because I need the entries to be initialised first. If its not initialised first then spinner.getAdapter() will be null when setSelectedItem is first called. Which means that previous saved selection will not be restored.

chaser
  • 3,107
  • 4
  • 29
  • 34

1 Answers1

25

There is no guaranteed order of execution in Android Data Binding. Because of this, you should merge binding adapters that have reliance on multiple attributes. In your case, you need to merge the binding adapter for selectedItem and entries.

@BindingAdapter(value = {"selectedItem", "entries"}, requireAll = false)
public static void setSelectedItem(Spinner spinner, SpinnerItem spinnerItem,
        List<? extends SpinnerItem> spinnerItems) {
    // Set entries attribute when provided
    if (spinnerItems != null) {
        for (int i = 0; i < spinnerItems.size(); i++) {
            spinnerItems.get(i).setIndex(i);
        }
        ArrayAdapter<? extends SpinnerItem> adapter =
            new ArrayAdapter<>(spinner.getContext(), R.layout.spinner_item, spinnerItems);
        spinner.setAdapter(adapter);
    }
    // set selectedItem attribute when provided
    if (spinnerItem != null) {
        if (spinner.getAdapter() == null) {
            return;
        }
        // Other code omitted for simplicity
    }
}
George Mount
  • 20,708
  • 2
  • 73
  • 61
  • @George Mount i have a requirement where the order of execution is very important , i have a TextSwitcher where i set Factory using binding function and set text . now setFactory binding method should run first then one setText how this could be achieved? – Velmurugan V Aug 18 '19 at 09:15