How to perform two-way data binding with a ToggleButton?

前端 未结 4 1465
花落未央
花落未央 2020-12-29 22:41

I have an ObservableBoolean field in my activity class, which is bound to the \"checked\" attribute of my ToggleButton like so:

and         


        
相关标签:
4条回答
  • 2020-12-29 23:23

    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>
            <variable type="com.example.myapp.User" name="user"/>
        </data>
        <RelativeLayout ...>
            <EditText android: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.

    0 讨论(0)
  • 2020-12-29 23:30

    Here are ways to set OnCheckedChangeListener in data binding:

    (1) Set by method expression

    In layout

    <variable
        name="activity"
        type="com.innovanathinklabs.sample.activities.CalendarActivity"/>
    
    <ToggleButton
        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"/>
    
    <ToggleButton
        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"/>
    
    <ToggleButton
        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"/>
    
    <ToggleButton
        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]")
        }
    }
    

    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.

    0 讨论(0)
  • 2020-12-29 23:36

    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;
    
    public class Item extends BaseObservable {
        private Boolean checked;
        @Bindable
        public Boolean getChecked() {
            return this.checked;
        }
        public void setChecked(Boolean checked) {
            this.checked = checked;
            notifyPropertyChanged(BR.checked);
        }
    }
    

    MainActivity.java:

    public class MainActivity extends AppCompatActivity {
    
        public Item item;
        ActivityMainBinding binding;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            item = new Item();
            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);
        }
    
        public void button_onClick(View v) {
            item.setChecked(!item.getChecked());
        }
    }
    

    activity_main.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
        <data>
            <variable
                name="item"
                type="com.example.abc.twowaydatabinding.Item" />
        </data>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
    
            <Switch
                android:id="@+id/switch_test"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:checked="@={item.checked}" />
    
            <Button
                android: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

    0 讨论(0)
  • 2020-12-29 23:40

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

    public class User{
       private boolean checked;
    
       public boolean isChecked() {
           return checked;
       }
    
       public void setChecked(boolean checked) {
           this.checked = checked;
       }
    }
    

    perform Two-way binding on your ToggleButton.

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

    and fetch this value by using binding variable.

    binding.getUser().isChecked()
    
    0 讨论(0)
提交回复
热议问题