Joe Bloe
Joe Bloe

Reputation: 383

Android Databinding: bind RadioGroup's selected RadioButton index in included layout file to MutableLiveData<Integer>

I have a layout file defining a RadioGroup containing nine RadioButtons. This layout file is included in a fragment layout multiple times.

For each include of the RadioGroup layout, I would like to somehow bind the index of the selected RadioButton to a different MutableLiveData<Integer> variable in the fragment's ViewModel, each representing a user-chosen score on mutliple nine point scales.

Databinding is setup correctly and works for other parts (e.g. binding click handlers and view visibility to a MutableLiveData<Boolean>).

What I tried to do so far is pass my ViewModel variable and/or distinct MutableLiveData<Integer> variables to the included layout via

...
<data>
    <variable name="viewModel" type="my.package.MyViewModel"/>
    <variable name="someVariable" type="androidx.lifecycle.LiveData&lt;Integer&gt;"/>
</data>
...
<include layout="@layout/radiobuttons"
    app:viewModel="@{viewModel}"
    app:someVariable="@{someVariable}"/>

and using the same variable declarations inside the RadioGroup layout to be able to use them in there. I then tried binding to the RadioGroup's android:onItemSelected attribute using

android:onItemSelected="@{(parent, view, position, id) -> someVariable.setValue(position)}"

resulting in the databinding error: "Cannot find the setter for attribute 'android:onItemSelected' with parameter type lambda on android.widget.RadioGroup"

or adding the following to the RadioButtons

android:onClick="@{(view) -> viewModel.updateRadioButtonSelection(someVariable, /*INDEX HERE e.g.*/ 0)}"

but that gives the databinding error "cannot find method updateRadioButtonSelection(java.lang.Integer, int) in class my.package.MyViewModel", meaning that the MutableLiveData<Integer> isn't passed on as such, but it's contained type (I have a method public void updateRadioButtonSelection(MutableLiveData<Integer> variable, int value) in the ViewModel class).

Can I specify - in the layout - a binding of which RadioButton was selected to an int variable without some detour involving extra code and translating RadioButton ids to an index? A one-way binding would suffice - changing the ViewModel variables from code is not intended.

Upvotes: 1

Views: 2736

Answers (2)

Joe Bloe
Joe Bloe

Reputation: 383

I found a way to accomplish what I wanted. Though I'm not too happy with it, I will accept it if nothing else turns up.

Deˣ's answer was very helpful, the only part I didn't know how to do was bind different includes of the same layout to different ViewModel variables / methods. I added setter methods for my MutableLiveData<Integer> variables representing indices of chosen radio buttons in the ViewModel and declared the following variable in the layout to be included multiple times:

<data>
    <variable
        name="variableSetter"
        type="java.util.function.Consumer&lt;Integer&gt;" />
</data>

This allows me to specifiy a different setter method to be called (via

android:onCheckedChanged="@{(group, checkedId) -> variableSetter.accept(group.indexOfChild(group.findViewById(checkedId)))}"

) for every include like so:

<include layout="@layout/..."
    app:method="@{viewModel::setQuestion1}" />

Upvotes: 1

Deˣ
Deˣ

Reputation: 4371

It's onCheckedChanged, not onItemSelected.

I don't think you need to send the variable to xml if you already have your ViewModel and the variable defined there. Just call the respective methods to update the value.

Dummy ViewModel content:

  private final ObservableInt firstInflateOptionSelected = new ObservableInt();
  private final ObservableInt secondInflateOptionSelected = new ObservableInt();
  ....
  ..
  .

  public void setFirstInflateOptionSelected(int value) {
    this.firstInflateOptionSelected.set(value);
  }

  public void setSecondInflateOptionSelected(int value) {
    this.secondInflateOptionSelected.set(value);
  }

Now in xml, do like this in RadioGroup:

android:onCheckedChanged="@{(group, checkedId) -> viewModel.setFirstInflateOptionSelected(checkedId)}"

Also, the checkedId is the View id, not the position. If you want the position. You can convert the view Id as the child position for the RadioGroup.

android:onCheckedChanged="@{(group, checkedId) -> viewModel.setFirstInflateOptionSelected(group.indexOfChild(group.findViewById(checkedId)))}"

Upvotes: 2

Related Questions