onCheckedChanged会自动调用

88

我有一个开关在RecyclerView中,数据是从数据库检索后显示在RecyclerView中的。当打开RecyclerView时,我读取数据库,如果DB中的某个字段为"Y",则启用开关,否则禁用开关。 现在的问题是,开关被启用时,onCheckedChanged监听器也会被调用,我只想在用户手动设置开关时才调用onCheckedChanged。

打开RecyclerView时执行以下操作:

holder.enabledisable.setChecked(messengerRecord.get_is_valid().equalsIgnoreCase("Y"));

ViewHolder类:

public class viewHolder extends RecyclerView.ViewHolder implements CompoundButton.OnCheckedChangeListener{
public SwitchCompat enabledisable;
 public viewHolder(View v) {
            enabledisable = (SwitchCompat) v.findViewById(R.id.enabledisable);
            enabledisable.setOnCheckedChangeListener(this);
...................................
...................................

当recyclerView刚打开时调用的OncheckedChanged方法:

@Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            Log.v("ranjith","called oncheckedchanged");
            MessengerRecord rec;
            rec = dbHelper.getRecord(descview.getText().toString());
            switch (buttonView.getId()) {
                case R.id.enabledisable:
                    if (isChecked) {
                        rec.set_is_valid("Y");
                        dbHelper.updateRecord(rec);
                     }
}

在布局文件中:

<android.support.v7.widget.SwitchCompat
    android:layout_marginRight="16dp"
    android:layout_marginEnd="16dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:focusable="false"
    android:id="@+id/enabledisable"
    android:layout_alignRight="@+id/textview_to"
    android:layout_alignEnd="@+id/textview_to"
    android:layout_alignParentRight="true"
    android:layout_alignParentEnd="true"/>
10个回答

279

我们大家都遇到这个问题,但官方谷歌并没有对这个简单的问题给出答案,这很奇怪。

最简单的方法是检查:

buttonView.isPressed()

如果为真,表示用户已点击视图。

不需要使用全局变量。


5
这绝对有效。但是如果用户滑动开关,是否会触发任何事件?在这种情况下,如果用户滑动开关,则该方法将不起作用。 - dhun
感谢这个简单而伟大的解决方案。这也可以与“RadioButton”一起使用。对于“RadioGroup”,我已经写了这个答案 - Sufian
感谢@Surfian的提及:),我很高兴这对于RadioButton也起作用。 - Sulfkain
26
快速切换开关时,该解决方案无效。谷歌太蠢了。 - DàChún
1
您可以通过实现OnClickListener并使用SwitchCompat的isChecked()方法来手动处理,而不需要使用onCheckedChangeListener。 - SBotirov
是的,这没用,如果你快速滑动它会返回 false - user924

15

试一试

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

        if (buttonView.isPressed()) {
          ... //returns true, if user clicks the switch button        
        }}

2
如果滑动速度非常快,则此方法无效。 - Manuel
作为一名 Android 开发者已经有超过 10 年的时间了,从未听说过这个技巧,谢谢! - Boy

14

为了避免这个问题,我最终使用了SwitchCompat的子类。这样一来,在使用这个类的时候,我就不需要模板代码了。 如果你需要改变选中状态但是不想触发监听器,就使用setCheckedSilent而不是setChecked

import android.content.Context;
import android.os.Parcelable;
import android.support.v7.widget.SwitchCompat;
import android.util.AttributeSet;

/**
 * Created by emanuel on 30/5/16.
 */
public class Switch extends SwitchCompat {

    private OnCheckedChangeListener listener;

    public Switch(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
        this.listener = listener;
        super.setOnCheckedChangeListener(listener);
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        super.setOnCheckedChangeListener(null);
        super.onRestoreInstanceState(state);
        super.setOnCheckedChangeListener(listener);
    }

    public void setCheckedSilent(boolean checked) {
        super.setOnCheckedChangeListener(null);
        super.setChecked(checked);
        super.setOnCheckedChangeListener(listener);
    }
}

当在onViewCreated方法中设置onRestoreInstanceState并返回到先前的片段时,它也会触发监听。希望对您有所帮助!


6

RecyclerView 会回收视图,因此,在为新项目的 Switch 设置选中值时,之前附加的 OnCheckedChangeListener 可能会被触发。

在将新数据绑定到项目时:

   switch.setOnCheckedChangeListener(null) // remove any existing listener from recycled view
   switch.isChecked = [true/false] // will no longer trigger any callback to listener
   switch.setOnCheckedChangeListener { btnView, isChecked ->
       // do exiting stuff
   }

1
非常感谢您发布这篇文章。我在Recyclerview中有多个Switch视图,正好遇到了这个问题。在onBind()中首先删除任何现有的监听器解决了我的问题。 - ice_chrysler
你必须使用holder和tag,并且只设置一次监听器,反复在绑定上设置监听器是不好的。 - user924

2
这对我有效:
注:此处为代码块,无需翻译。
 boolean selected = preferences.isChecked();
        yourCheckBox.setOnCheckedChangeListener(new 
            CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, 
            boolean isChecked) {
                    if (buttonView.isPressed()) {
               preferences.setChecked(isChecked);
                    } else {
                        yourCheckBox.setChecked(selected);
                    }
                }
            });

1
如果滑动速度非常快,则此方法无效。 - Manuel

1

Kotlin开发者:

    checkboxXYZ.setOnCheckedChangeListener { btnView, isChecked ->
        if (btnView.isPressed) {

        }
    }

0

使用全局布尔变量,当从数据库读取数据时将其设置为“true”,在onCheckChange中检查(if)后再将其设置为“false”。在onCheckChange方法的开头检查此变量是否为false,执行代码,否则返回(此变量的默认值必须为false);

@Override
   public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
     if(!isSourceDB){ 

       Log.v("ranjith","called oncheckedchanged");
        MessengerRecord rec;
        rec = dbHelper.getRecord(descview.getText().toString());
        switch (buttonView.getId()) {
            case R.id.enabledisable:
                if (isChecked) {
                    rec.set_is_valid("Y");
                    dbHelper.updateRecord(rec);

          }
    }//end if
  isSourceDB = false; }// end oncheckedchange

这似乎是一个解决方法。我在寻找Android中的一个功能,它只在用户点击时调用oncheckedchanged(即获得焦点)。但它确实有效。我给你点赞。 - Psypher
@Ranjith:当单击开关时,首先调用toggle()方法,然后在toggle方法中调用setchecked()方法。因此,您应该将上述代码插入到toggle方法中而不是checkchanged中。顺便说一下,如果您只想在onCheckChange()中编写代码,那么您应该编写一个仅更改开关GUI状态的方法,并在从数据库读取数据时调用它。 - mk72

0

如果您正在适配器中更改状态,则

   holder.rawManageDriverLayoutBinding.rawManageDriverSwitch.setOnCheckedChangeListener(null);

setOnCheckedChangeListener之前。


-1

我在 ViewPager 屏幕中的片段内遇到了同样的问题。当我们在片段之间切换时,onCheckedChanged 监听器会再次被调用。

如果还有人在寻找此问题,请尝试一下。

@Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

            if (buttonView.isInTouchMode()) {
              ... //Your code will come here.         
            }
}

如果isInTouchMode被自动或手动更改,它总是为真,但没有用处。 - user924

-1

唯一的解决方案是在监听器中使用isPressed()方法


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