Skip to content Skip to sidebar Skip to footer

How To Perform Two-way Data Binding With A Togglebutton?

I have an ObservableBoolean field in my activity class, which is bound to the 'checked' attribute of my ToggleButton like so: android:checked='@{activity.editing}' I was hoping th

Solution 1:

You need another '=' to tell Android that you want to use Two-Way databinding:

android:checked="@={activity.editing}"

You can find more information on this in the wordpress article of George Mount:

Two-Way Data Binding

Android isn’t immune to typical data entry and it is often important to reflect changes from the user’s input back into the model. For example, if the above data were in a contact form, it would be nice to have the edited text pushed back into the model without having to pull the data from the EditText. Here’s how you do it:

<layout...><data><variabletype="com.example.myapp.User"name="user"/></data><RelativeLayout...><EditTextandroid:text="@={user.firstName}".../></RelativeLayout></layout>

Pretty nifty, eh? The only difference here is that the expression is marked with @={} instead of @{}. It is expected that most data binding will continue to be one-way and we don’t want to have all those listeners created and watching for changes that will never happen.

Solution 2:

No need to take ObservableBoolean, you can perform this operation by regular getter-setter method of boolean variable. Like this in your model class

publicclassUser{
   privateboolean checked;

   publicbooleanisChecked() {
       return checked;
   }

   publicvoidsetChecked(boolean checked) {
       this.checked = checked;
   }
}

perform Two-way binding on your ToggleButton.

<ToggleButtonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:checked="@={user.checked}"/>

and fetch this value by using binding variable.

binding.getUser().isChecked()

Solution 3:

Here's a simple example of a 2-way databinding using a Switch, which also has the Checked property, like the ToggleButton.

Item.java:

import android.databinding.BaseObservable;
import android.databinding.Bindable;

publicclassItemextendsBaseObservable {
    privateBoolean checked;
    @BindablepublicBooleangetChecked() {
        returnthis.checked;
    }
    publicvoidsetChecked(Boolean checked) {
        this.checked = checked;
        notifyPropertyChanged(BR.checked);
    }
}

MainActivity.java:

publicclassMainActivityextendsAppCompatActivity {

    publicItem item;
    ActivityMainBinding binding;

    @OverrideprotectedvoidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        item = newItem();
        item.setChecked(true);

        /* By default, a Binding class will be generated based on the name of the layout file,
        converting it to Pascal case and suffixing “Binding” to it.
        The above layout file was activity_main.xml so the generate class was ActivityMainBinding */

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setItem(item);
    }

    publicvoidbutton_onClick(View v) {
        item.setChecked(!item.getChecked());
    }
}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?><layoutxmlns:android="http://schemas.android.com/apk/res/android"><data><variablename="item"type="com.example.abc.twowaydatabinding.Item" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><Switchandroid:id="@+id/switch_test"android:layout_width="wrap_content"android:layout_height="wrap_content"android:checked="@={item.checked}" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="change"android:onClick="button_onClick"/></LinearLayout></layout>

build.gradle:

android {
...
    dataBinding{
        enabled=true
    }

}

Source documentation: https://developer.android.com/topic/libraries/data-binding/index.html

Solution 4:

Here are ways to set OnCheckedChangeListener in data binding:

(1) Set by method expression

In layout

<variablename="activity"type="com.innovanathinklabs.sample.activities.CalendarActivity"/><ToggleButtonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:checked="@={model.checked}"android:onCheckedChanged="@{activity::onGenderChanged}"
    />

In Activity

classHomeActivity : AppCompatActivity() {
    overridefunonCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DataBindingUtil.setContentView<ActivityCalendarBinding>(this, R.layout.activity_calendar)
        binding.activity = this
        binding.model = Model()
    }

    funonGenderChanged(buttonView: CompoundButton, isChecked: Boolean) {
        println("buttonView = [$buttonView], isChecked = [$isChecked]")
    }
}

(2) Set by lambda expression and method call

<variablename="model"type="com.innovanathinklabs.sample.data.Model"/><variablename="activity"type="com.innovanathinklabs.sample.activities.HomeActivity"/><ToggleButtonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:checked="@={model.checked}"android:onCheckedChanged="@{(button, bool)-> activity.saveGender(bool)}"
    />

In Activity

classHomeActivity : AppCompatActivity() {
    overridefunonCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DataBindingUtil.setContentView<ActivityCalendarBinding>(this, R.layout.activity_calendar)
        binding.activity = this
        binding.model = Model()
    }

    funsaveGender(isChecked: Boolean) {
        println("isChecked = [$isChecked]")
    }
}

(3) Pass OnCheckedChangeListener anonymous class to layout

<variablename="onGenderChange"type="android.widget.CompoundButton.OnCheckedChangeListener"/><ToggleButtonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:checked="@={model.checked}"android:onCheckedChanged="@{onGenderChange}"
    />

In Activity

classHomeActivity : AppCompatActivity() {
    overridefunonCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DataBindingUtil.setContentView<ActivityCalendarBinding>(this, R.layout.activity_calendar)
        binding.model = Model()
        binding.setOnGenderChange { buttonView, isChecked ->
            println("buttonView = [$buttonView], isChecked = [$isChecked]")
        }
    }
}

(4) Pass OnCheckedChangeListener by reference

<variablename="onGenderChange2"type="android.widget.CompoundButton.OnCheckedChangeListener"/><ToggleButtonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:checked="@={model.checked}"android:onCheckedChanged="@{onGenderChange2}"
    />

Activity

classHomeActivity : AppCompatActivity() {
    overridefunonCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DataBindingUtil.setContentView<ActivityCalendarBinding>(this, R.layout.activity_calendar)
        binding.model = Model()
        binding.onGenderChange2 = onGenderChange
    }

    privateval onGenderChange: CompoundButton.OnCheckedChangeListener = CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
        println("buttonView = [$buttonView], isChecked = [$isChecked]")
    }
}

This will never work

Because you can't set two callback on one component. One callback is already set by two way binding, so your callback will not work.

binding.toggleButton.setOnCheckedChangeListener { buttonView, isChecked ->
    println("buttonView = [$buttonView], isChecked = [$isChecked]")
}

Check CompoundButtonBindingAdapter class to see how Switch Binding works.

Post a Comment for "How To Perform Two-way Data Binding With A Togglebutton?"