Android数据绑定:如何避免程序触发的onCheckedChanged事件?

21

我现在正在尝试在我的项目中使用Android数据绑定,并遇到了这种问题,例如:我有3个复选框作为一个复选框组,如果第一个复选框被选中,则类型变量将为1。第二个将使类型变为2,第三个将使类型变为3。所以我以这种方式实现了代码。

   // layout.xml


   <android.support.v7.widget.AppCompatCheckBox
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:checked="@{userInfoViewModel.type == 1}"
        android:onCheckedChanged="@{(compoundButton, checked) -> userInfoViewModel.onTypeChecked(checked, 1)}"
        />

   <android.support.v7.widget.AppCompatCheckBox
        android:layout_width="50dp"
        android:layout_height="55dp"
        android:checked="@{userInfoViewModel.type == 2}"
        android:onCheckedChanged="@{(compoundButton, checked) -> userInfoViewModel.onTypeChecked(checked, 2)}"
        />

    <android.support.v7.widget.AppCompatCheckBox
        android:layout_width="50dp"
        android:layout_height="55dp"
        android:checked="@{userInfoViewModel.type == 3}"
        android:onCheckedChanged="@{(compoundButton, checked) -> userInfoViewModel.onTypeChecked(checked, 3)}"
        />

// viewModel

public void onTypeChecked(boolean checked, int i) {
    if (checked) {
        // if it is a check. set the type
        type.set(i);
    } else {
        // if it is a uncheck. set type to unknown
        type.set(0);
    }
}
现在的问题是,如果我先勾选了第一个复选框,然后再勾选第二个复选框。type应该设置为2,并且UI应该正确更新。但实际情况是,在type设置为2之后,uncheck事件也会在第一个复选框上触发,然后type.set(0)被触发,因此没有任何复选框被选中。
事实上,这个问题与onCheckedChanged called automatically是一样的。我需要的是数据绑定的解决方案。
在非数据绑定项目中,我认为最好的解决方案是使用setCheckedSilent(由@Emanuel Andrada提供的答案)。
  public void setCheckedSilent(boolean checked) {
    super.setOnCheckedChangeListener(null);
    super.setChecked(checked);
    super.setOnCheckedChangeListener(listener);
}

但是在数据绑定中,我无法做到这一点。所以有没有专家可以帮我解决?

根据@Arpan Sharma的答案,改为监听onClick而不是onCheckedChanged。这个解决方案目前有效,但我担心checked的值是否总是正确的?

public void onTypeChecked(View view, int i) {
    Boolean checked = ((CheckBox) view).isChecked();

    if (checked) {
        type.set(i);
    } else {
        type.set(0);
    }
}
6个回答

15

在ViewModel中公开一个ObservableBoolean,然后使用双向数据绑定来控制该布尔值。

这样,您就可以使用ObservableBoolean的值来决定要做什么,而不是在XML中编码。

android:checked="@={vm.checkedValue}"

请展示一些代码。我无法理解你做了什么? - CoolMind
@CoolMind 缺失的代码只是 private val observableBoolean = ObservableBoolean(false) - EpicPandaForce

8

我曾经面临同样的问题,我使用了onClick监听器而不是onCheck监听器。这样,当以编程方式设置时,监听器不会更改选中状态。在你的问题中,你应该尝试为你的复选框设置不同的选中状态变化监听器。


1
那么,你如何区分它是选中还是未选中?如何在处理时获取当前状态?它应该能够正常工作。顺便说一句,谷歌真的很愚蠢吗?没有优雅的解决方案吗? - Leon
我已经尝试了你的解决方案(请查看问题的更新),它在我的环境中可以工作。但是我正在考虑复选框的状态是否总是正确的。当单击回调被触发时,复选框的状态是否总是更新? - Leon
是的,复选框的状态将自动更改。在监听器内部,您将需要检查其状态,然后相应地采取行动。 - Arpan Sharma
每次单击都会进行选中/取消操作。不用担心。 - Arpan Sharma
是的,状态应该每次都会改变,但我担心的是:状态改变是否总是在单击回调被调用之前发生。 - Leon
好的。谢谢您,请等待几天时间。如果没有更好的答案,我会选择您的回答。 - Leon

8

使用数据绑定非常简单

在XML复选框组件中

 <CheckBox
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onCheckedChanged="@{(compoundButton, checked) -> 
changeKeyboardVM.onCheckedChange(compoundButton, checked)}" />

在ViewModel或Activity中。
  public void onCheckedChange(CompoundButton button, boolean check) {
    Log.d("Z1D1", "onCheckedChange: " + check);
}

现在布尔值检查中,true 表示选中,false 表示未选中。


1
你没有解决问题,因为你没有设置 android:checked - CoolMind

7

我第一次遇到这个问题,我认为最好使用绑定适配器来实现。

这是绑定适配器的代码:

interface OnUserCheckedChangeListener {
    fun onUserCheckChange(view:CompoundButton, isChecked:Boolean)
}
@BindingAdapter("onUserCheckedChange")
fun setUserCheckedChangeListener(view:CompoundButton, listener: OnUserCheckedChangeListener?){
    if(listener == null){
        view.setOnClickListener(null)
    }else{
        view.setOnClickListener {
            listener.onUserCheckChange(view, view.isChecked)
        }
    }
}

我们可以在任何复合按钮上使用它

<CheckBox
            android:id="@+id/finish_check"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"   
            android:checked="@{your_condition}"
            onUserCheckedChange="@{(view, checked) -> vm.onItemChecked(todo, checked)}"
            />

它如何帮助?onUserCheckedChangeandroid:checked 设置后被调用吗?当滑动时,它是否会触发 onCheckedChanged 事件? - CoolMind

1
使用 onClick 代替 onCheckedChanged 避免双向绑定。 来自 item_order_view.xml:
<data>

    <variable
        name="viewModel"
        type="com.package.name.OrderItemViewModel" />
</data>

           <CheckBox
                android:id="@+id/cb_selected"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginLeft="8dp"
                android:buttonTint="@color/white"
                android:checked="@{viewModel.isSelect}"
                android:onClick="@{() -> viewModel.onClickedCheckBox()}"
                android:textColor="@color/white" />

来自OrderItemViewModel.java

public class OrderItemViewModel {

    public final ObservableField<Boolean> isSelect;
    public final OrderItemViewModelListener mListener;
    private final Order mOrder;

    public OrderItemViewModel(Order order, OrderItemViewModelListener listener) {
        this.mListener = listener;
        this.mOrder = order;

        isSelect = new ObservableField<>(mOrder != null ? mOrder.isSelect() : false);
    }

    /**
     * Using onClick instead of onCheckedChanged
     * to prevent 2-ways binding issue.
     */
    public void onClickedCheckBox() {
        mListener.onCheckChanged();
    }

    public interface OrderItemViewModelListener {
        void onCheckChanged();
    } 
}

你如何处理滑动操作? - CoolMind

0

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