问题
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 this would create a two-way relationship between the button and the boolean, but it only carries changes from the field to the button, not the other way. What am I doing wrong, or is this not in the scope of Android DataBinding?
回答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>
<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.
回答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
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()
回答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;
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
回答4:
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.
来源:https://stackoverflow.com/questions/37582267/how-to-perform-two-way-data-binding-with-a-togglebutton