How to use data binding for Switch onCheckedChageListener event?

27,873

Solution 1

You can do it with a method reference:

<CheckBox android:onCheckedChanged="@{callback::checkedChangedListener}".../>

or with a lambda expression if you want to pass different parameters:

<CheckBox android:onCheckedChanged="@{() -> callback.checked()}".../>

Solution 2

Using lambda expression and a Switch:

public void onCheckedChanged(boolean checked) {
     // implementation      
}

XML file:

<android.support.v7.widget.SwitchCompat
    android:onCheckedChanged="@{(switch, checked) -> item.onCheckedChanged(checked)}"
    ...
/>

Where item is the class that implements onCheckedChange method and is imported to the XML file like this:

<data>
    <variable
        name="item"
        type="yourClass"/>
</data>

Solution 3

Different Ways

(1) Set by method expression

In layout

<variable
    name="activity"
    type="com.innovanathinklabs.sample.activities.CalendarActivity"/>

<Switch
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:checked="@={model.checked}"
    android:onCheckedChanged="@{activity::onGenderChanged}"
    />

In Activity

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

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

(2) Set by lambda expression and method call

<variable
    name="model"
    type="com.innovanathinklabs.sample.data.Model"/>

<variable
    name="activity"
    type="com.innovanathinklabs.sample.activities.HomeActivity"/>

<Switch
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:checked="@={model.checked}"
    android:onCheckedChanged="@{(button, bool)-> activity.saveGender(bool)}"
    />

In Activity

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

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

(3) Pass OnCheckedChangeListener anonymous class to layout

<variable
    name="onGenderChange"
    type="android.widget.CompoundButton.OnCheckedChangeListener"/>

<Switch
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:checked="@={model.checked}"
    android:onCheckedChanged="@{onGenderChange}"
    />

In Activity

class HomeActivity : AppCompatActivity() {
    override fun onCreate(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

<variable
    name="onGenderChange2"
    type="android.widget.CompoundButton.OnCheckedChangeListener"/>

<Switch
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:checked="@={model.checked}"
    android:onCheckedChanged="@{onGenderChange2}"
    />

Activity

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

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

Now below will NOT work

Now if you set CheckChangeListener in code that will not work. because you can't set two callback on one component. One callback is already set by binding, so your callback in code will not work.

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

Check CompoundButtonBindingAdapter class to see how Switch Binding works.

Solution 4

Simply use ViewModel, liveData and Transformations first on your ViewModel

var availabilityState = MutableLiveData<Boolean>(false)
val availabilityText :LiveData<String> =Transformations.map(availabilityState)
{
    if (it == true)
    {
        "Available"
    }else
    {
      "Not Available"
    }
}

Then on your XML use

<Switch
android:checked="@={addBookViewModel.availabilityState}"
android:text="@{addBookViewModel.availabilityText}"
...>

That is it.

Share:
27,873
Mohanish Nerurkar
Author by

Mohanish Nerurkar

Updated on July 09, 2022

Comments

  • Mohanish Nerurkar
    Mohanish Nerurkar almost 2 years

    As question indicates, how to bind checked change listener to Switch button in xml ?

    I am not using recycler view. Just a simple layout.

    Any help appreciated.

  • Tiago Oliveira
    Tiago Oliveira over 7 years
    This is not working for me i get Unknown attribute android:onCheckedChanged
  • arekolek
    arekolek almost 7 years
    @TiagoOliveira although I'm also getting that warning, after launching the app it seems to work indeed
  • Aditya Ladwa
    Aditya Ladwa almost 7 years
    Can we create a custom 2-way binding which would reduce the boilerplate
  • David
    David almost 7 years
    @GeorgeMount why we can't use app:onCheckedChangeListener to call method setOnCheckedChangeListener in CheckBox?
  • Hossein Shahdoost
    Hossein Shahdoost over 6 years
    @AdityaLadwa what's wrong with using checked attribute? you can bind that pretty easily
  • Danilo Mz
    Danilo Mz over 5 years
    The last statement saved my day. Apparently I had 2 listeners on a RadioGroup and was wondering why the binding implementation wasn't working
  • Elliptica
    Elliptica about 5 years
    Is there a way to do this just using xml? I.e. if I have a SwitchPreference in an xml and I want it to use a custom layout android:layout="@layout/custom" and pass that layout a data-bound title "passedTitle", I can do it successfully using <include> as follows: <include layout="@layout/custom" bind:passedTitle="@{@string/title}" />, but it doesn't do anything (I get attribute passedTitle not found) if I just do it in the preference: <SwitchPreference android:key="@string/key" layout="@layout/custom" bind:passedTitle="@{@string/title}"
  • adi
    adi about 4 years
    But after using this, if I try to check my switch programmatically using the binding.mySwitch.setChecked(true), the item.onCheckedChanged method is not getting called !!!
  • kgandroid
    kgandroid almost 4 years
    Beautiful answer.used the last one
  • Alessandro Caliaro
    Alessandro Caliaro almost 4 years
    Also works with androidx.appcompat.widget.SwitchCompat
  • Shawn
    Shawn almost 4 years
    To bad, I can't give plus one for each solution you submitted.
  • Ajit Kumar Dubey
    Ajit Kumar Dubey about 3 years
    Cannot find a setter for <com.google.android.material.switchmaterial.SwitchMaterial app:onCheckedChanged> that accepts parameter type 'lambda'