android数据绑定:如何避免以编程方式触发的onCheckedChanged

Posted

技术标签:

【中文标题】android数据绑定:如何避免以编程方式触发的onCheckedChanged【英文标题】:android databinding: how to avoid onCheckedChanged triggered by programmatically 【发布时间】:2017-07-28 17:44:19 【问题描述】:

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

   // layout.xml


   <android.support.v7.widget.AppCompatCheckBox
        android:layout_
        android:layout_
        android:checked="@userInfoViewModel.type == 1"
        android:onCheckedChanged="@(compoundButton, checked) -> userInfoViewModel.onTypeChecked(checked, 1)"
        />

   <android.support.v7.widget.AppCompatCheckBox
        android:layout_
        android:layout_
        android:checked="@userInfoViewModel.type == 2"
        android:onCheckedChanged="@(compoundButton, checked) -> userInfoViewModel.onTypeChecked(checked, 2)"
        />

    <android.support.v7.widget.AppCompatCheckBox
        android:layout_
        android:layout_
        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 应正确更新。但实际情况是uncheck事件也发生在第一个checkbox上,type设置为2后,type.set(0)被触发,所以没有checkbox。

其实这个问题和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);
    

【问题讨论】:

【参考方案1】:

从 ViewModel 公开一个 ObservableBoolean,然后在该布尔值上使用双向数据绑定。

然后你可以使用 ObservableBoolean 的值来决定你想要做什么,而不是在 XML 中编码。

android:checked="@=vm.checkedValue"

【讨论】:

请显示一些代码。我不明白,你做了什么? @CoolMind 缺少的代码只是private val observableBoolean = ObservableBoolean(false)【参考方案2】:

数据绑定非常简单

在xml复选框组件中

 <CheckBox
            android:layout_
            android:layout_
            android:onCheckedChanged="@(compoundButton, checked) -> 
changeKeyboardVM.onCheckedChange(compoundButton, checked)" />

在 ViewModel 或 Activity 中

  public void onCheckedChange(CompoundButton button, boolean check) 
    Log.d("Z1D1", "onCheckedChange: " + check);

现在布尔检查 true 已检查 和 false 未选中

【讨论】:

你没有解决问题,因为你没有设置android:checked【参考方案3】:

我遇到了同样的问题,我使用了 onCLick 监听器而不是 onCHeck 侦听器。这样侦听器在以编程方式设置时不会更改检查状态。 在您的问题中,您应该尝试为您的复选框设置不同的检查更改侦听器。

【讨论】:

那么如何区分是选中还是取消选中?处理时获取当前状态?它应该能够工作。顺便说一句,谷歌是如此愚蠢?没有优雅的解决方案? 我已经尝试了您的解决方案(检查问题的更新),它适用于我的环境。但我正在考虑复选框的状态是否总是正确的。当点击回调被触发时,复选框的状态总是更新? 是的,复选框的状态将自动更改,在侦听器内部,您必须检查其状态然后采取相应措施。 每次点击都会勾选/取消勾选。不用担心 是的,每次状态都应该改变,但我担心的是:状态改变是否总是在onclick回调被调用之前发生。【参考方案4】:

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

这是绑定适配器的代码

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_
            android:layout_
            app:layout_constraintStart_toStartOf="parent"   
            android:checked="@your_condition"
            onUserCheckedChange="@(view, checked) -> vm.onItemChecked(todo, checked)"
            />

【讨论】:

它有什么帮助? onUserCheckedChange 是否在 android:checked 设置后调用?滑动时会引发onCheckedChanged 事件吗?【参考方案5】:

使用onClick 代替onCheckedChanged 来防止双向绑定。

来自item_order_view.xml

<data>

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

           <CheckBox
                android:id="@+id/cb_selected"
                android:layout_
                android:layout_
                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();
     

【讨论】:

如何处理滑动?【参考方案6】:

见https://***.com/a/52606437/2914140:

OnCheckedChangeListener里面写:

if (button.isPressed()) 
    // A user pressed Switch.

也许How to use data binding for Switch onCheckedChageListener event? 的一些答案可能会有所帮助,例如,定义android:onCheckedChanged 侦听器,但我没有测试。

【讨论】:

以上是关于android数据绑定:如何避免以编程方式触发的onCheckedChanged的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式避免android手机中的深度睡眠模式?

我可以以编程方式触发淘汰视图模型更新吗?

如何以编程方式触发科尔多瓦的后退按钮事件

单击按钮时如何以编程方式触发刷新primeNG数据表

PWA:如何以编程方式触发:“添加到主屏幕”?在 iOS Safari 上

当我以编程方式更改用户控件时,如何让数据绑定双向工作?