如何使用ToggleButton实现双向数据绑定?

26

我在活动类中有一个ObservableBoolean字段,它与我的ToggleButton的"checked"属性绑定,如下所示:

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

我本希望这将创建一个按钮和布尔值之间的双向关系,但它只能从字段传递更改到按钮,而不能反过来。我做错了什么,或者这不在 DataBinding 的范围内?

4个回答

47

你需要再加一个等号告诉Android你想要使用双向数据绑定:

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

您可以在George Mount的WordPress文章中找到更多关于双向数据绑定的信息

双向数据绑定

Android并不免疫典型的数据录入,将用户输入的更改反映回模型通常很重要。例如,如果以上数据在联系表单中,则很好地将编辑后的文本推回模型,而无需从EditText提取数据。这是如何做到的:

<layout ...>
    <data>
        <variable type="com.example.myapp.User" name="user"/>
    </data>
    <RelativeLayout ...>
        <EditText android:text="@={user.firstName}" .../>
    </RelativeLayout>
</layout>

很不错,是吗?唯一的区别在于这个表达式标记为@={}而不是@{}。预计大多数数据绑定仍将是单向的,我们不希望创建所有这些监听器并观察永远不会发生变化的内容。


这个答案很棒。 - Amit Bhandari

5
无需使用ObservableBoolean,在您的模型类中可以通过常规的getter-setter方法来执行此操作。像这样:
public class User{
   private boolean checked;

   public boolean isChecked() {
       return checked;
   }

   public void setChecked(boolean checked) {
       this.checked = checked;
   }
}

在您的 ToggleButton 上执行双向绑定。

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

并使用绑定变量来获取该值。
binding.getUser().isChecked()

2
你在isChecked()方法中缺少了@Bindable注解,而且在setter方法中也没有调用notifyPropertyChanged(BR.checked) - saiyancoder

4

以下是设置数据绑定中的 OnCheckedChangeListener 的方法:

(1) 通过 方法表达式 设置

在布局文件中设置

<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}"
    />

在活动中
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) 通过lambda表达式和方法调用设置

<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)}"
    />

在活动中
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) 将OnCheckedChangeListener匿名类传递给布局

<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}"
    />

在活动中
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)通过引用传递 OnCheckedChangeListener

<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}"
    />

活动

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]")
    }
}

这永远不会起作用

因为你无法在一个组件上设置两个回调函数。由于双向绑定已经设置了一个回调函数,所以你的回调函数将不起作用。

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

查看 CompoundButtonBindingAdapter 以了解 Switch 绑定的工作方式。


4
这里有一个简单的例子,使用开关实现双向数据绑定,开关还有类似于ToggleButton的Checked属性。
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
    }

}

源文件文档:https://developer.android.com/topic/libraries/data-binding/index.html

该文档介绍了Android中数据绑定库的使用方法和优势。通过数据绑定,您可以将UI组件与应用程序数据模型绑定在一起,从而确保UI组件及时反应数据模型的变化。数据绑定还提供了类型安全、可读性好且易于维护的代码,以及减少手动更新UI的工作量等优点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接