android.widget.Switch - 开/关事件监听器?

297

我想要实现一个开关按钮,android.widget.Switch(从API v.14可用)。

<Switch
    android:id="@+id/switch1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Switch" />

但是我不确定如何为按钮添加事件监听器。这应该是一个"onClick"监听器吗?如果它被切换为"on",我怎么知道呢?


6
通过XML的OnClick实际上可以工作 - 但只适用于对按钮的“点击”,而不是“滑动”。 - m02ph3u5
11个回答

593

Switch 继承了 CompoundButton 的属性,因此我建议使用 OnCheckedChangeListener

mySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        // do something, the isChecked will be
        // true if the switch is in the On position
    }
});

3
没问题。我不知道你怎么看,但我希望他们将其命名为OnCheckChangedListener,与OnItemSelectedListener类似,因为On-名词-动词-Listener是一种已经确立的命名约定。 - Sam
2
但是,当你将其放在片段上时,例如,如果你将开关设置为打开状态,那么该事件总是会在你重新访问该片段时触发。 - Lendl Leyba
1
@Sam 如果我想使用setChcked()方法将开关状态更改为开启或关闭,而不想执行onCheckedChanged方法,有什么办法可以实现吗?但是当用户再次点击开关时,onCheckedChanged方法会被执行...有没有什么方法可以解决这个问题? - Deepak Kumar
3
按钮有“OnClick”,开关没有“OnChange”!谷歌团队设计得很好! - Vassilis
@deepakkumar 找到了在不执行 onCheckChange 的情况下切换开关的解决方案吗? - KZoNE
2
@KZoNE 我在这里所做的是使用点击监听器来改变状态,并将开关传递给方法(在我的情况下是 API 调用),然后使用 setChecked() 方法来改变状态(就像在 API 调用的 onFailure/onError 中一样)。希望这可以帮助到你。 - Deepak Kumar

59

使用以下代码片段通过 XML 将开关添加到您的布局中:

<Switch
     android:id="@+id/on_off_switch"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:textOff="OFF"
     android:textOn="ON"/>

然后在你的Activity的onCreate方法中,获取你的Switch的引用并设置它的OnCheckedChangeListener:

Switch onOffSwitch = (Switch)  findViewById(R.id.on_off_switch); 
onOffSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    Log.v("Switch State=", ""+isChecked);
}       

});

3
这是一个更清晰的答案,提供了布局和代码以配合。 - AshesToAshes
如何在单个监听器中管理多个SwitchCompat? 请为此提供答案。 - Anand Savjani

45

对于使用 Kotlin 的人,您可以按照以下方式为一个开关(在此示例中具有 ID mySwitch)设置监听器:

mySwitch.setOnCheckedChangeListener { _, isChecked ->
    // do whatever you need to do when the switch is toggled here
}

isChecked 为 true 表示开关当前处于选中状态(ON),否则为 false。


20

定义你的XML布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.neoecosystem.samplex.SwitchActivity">

    <Switch
        android:id="@+id/myswitch"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content" />

</RelativeLayout> 

然后创建一个活动

public class SwitchActivity extends ActionBarActivity implements CompoundButton.OnCheckedChangeListener {

    Switch mySwitch = null;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_switch);


        mySwitch = (Switch) findViewById(R.id.myswitch);
        mySwitch.setOnCheckedChangeListener(this);
    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if (isChecked) {
            // do something when check is selected
        } else {
            //do something when unchecked
        }
    }

    ****
}

======== 对于低于 API 14,请使用 SwitchCompat =========

XML

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.neoecosystem.samplex.SwitchActivity">

    <android.support.v7.widget.SwitchCompat
        android:id="@+id/myswitch"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content" />

</RelativeLayout>

活动

public class SwitchActivity extends ActionBarActivity implements CompoundButton.OnCheckedChangeListener {

    SwitchCompat mySwitch = null;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_switch);


        mySwitch = (SwitchCompat) findViewById(R.id.myswitch);
        mySwitch.setOnCheckedChangeListener(this);
    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if (isChecked) {
            // do something when checked is selected
        } else {
            //do something when unchecked
        }
    }
   *****
}

5
不要忘记检查 buttonView.isPressed()。 - JacksOnF1re
@JacksOnF1re 有一个好方法可以仅捕获用户输入,而无需进行编程建立。 - Aksenov Vladimir

8
在 Kotlin 中:
        switch_button.setOnCheckedChangeListener { buttonView, isChecked ->
        if (isChecked) {
            // The switch enabled
            text_view.text = "Switch on"

        } else {
            // The switch disabled
            text_view.text = "Switch off"

        }
    }

1
这对于开关切换是没问题的,但是如何获取当前状态呢? - Skovie
@Skovie 布尔状态 = switch_button.isChecked() - Shadros

5
Switch小部件的布局大致如下。
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <Switch
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="20dp"
        android:gravity="right"
        android:text="All"
        android:textStyle="bold"
        android:textColor="@color/black"
        android:textSize="20dp"
        android:id="@+id/list_toggle" />
</LinearLayout>

在Activity类中,您可以通过两种方式进行编码。根据使用情况,您可以进行编码。
第一种方法:
public class ActivityClass extends Activity implements CompoundButton.OnCheckedChangeListener {
Switch list_toggle;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.return_vehicle);

    list_toggle=(Switch)findViewById(R.id.list_toggle);
    list_toggle.setOnCheckedChangeListener(this);
    }
}

public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
    if(isChecked) {
        list_toggle.setText("Only Today's");  //To change the text near to switch
        Log.d("You are :", "Checked");
    }
    else {
        list_toggle.setText("All List");   //To change the text near to switch
        Log.d("You are :", " Not Checked");
    }
}

第二种方法
public class ActivityClass extends Activity {
Switch list_toggle;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.return_vehicle);

    list_toggle=(Switch)findViewById(R.id.list_toggle);
    list_toggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
       @Override
       public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
          if(isChecked) {
             list_toggle.setText("Only Today's");  //To change the text near to switch
             Log.d("You are :", "Checked");
          }
          else {
             list_toggle.setText("All List");  //To change the text near to switch
             Log.d("You are :", " Not Checked");
          }
       }       
     });
   }
}

5

2020年9月 - 编程方式回答

您可以使用编程方式来处理Switch Widget和Material Design:

Switch yourSwitchButton = findViewById(R.id.switch_id);

yourSwitchButton.setChecked(true); // true is open, false is close.

yourSwitchButton.setOnCheckedChangeListener((compoundButton, b) -> {
        if (b){
          //open job.
        }  
        else  {
          //close job.
        }
    });

4
您可以使用DataBinding和ViewModel来处理Switch Checked Change事件。
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
                name="viewModel"
                type="com.example.ui.ViewModel" />
    </data>
    <Switch
            android:id="@+id/on_off_switch"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onCheckedChanged="@{(button, on) -> viewModel.onCheckedChange(on)}"
     />


1
android.widget.Switch没有onCheckedChanged属性。 你会得到以下错误: AAPT:错误:找不到属性android:onCheckedChanged。 - Sold Out

1
我的解决方案是使用 SwitchCompat 和 Kotlin。在我的情况下,我只需要在用户通过 UI 触发更改时才对其进行反应。实际上,我的开关会对一个 LiveData 进行反应,这使得 setOnClickListenersetOnCheckedChangeListener 都无法使用。实际上,setOnClickListener 对用户交互做出了正确的反应,但如果用户拖动开关,它就不会被触发。另一方面,setOnCheckedChangeListener 也会在程序切换开关时触发(例如由观察者触发)。现在,在我的情况下,该开关存在于两个片段中,因此在某些情况下,onRestoreInstanceState 会触发带有旧值的开关,从而覆盖了正确的值。

所以,我查看了 SwitchCompat 的代码,并成功地模仿了它区分单击和拖动的行为,并使用它来构建一个自定义的触摸侦听器,使其正常工作。以下是代码:

/**
 * This function calls the lambda function passed with the right value of isChecked
 * when the switch is tapped with single click isChecked is relative to the current position so we pass !isChecked
 * when the switch is dragged instead, the position of the thumb centre where the user leaves the
 * thumb is compared to the middle of the switch, and we assume that left means false, right means true
 * (there is no rtl or vertical switch management)
 * The behaviour is extrapolated from the SwitchCompat source code
 */
class SwitchCompatTouchListener(private val v: SwitchCompat, private val lambda: (Boolean)->Unit) :  View.OnTouchListener {
    companion object {
        private const val TOUCH_MODE_IDLE = 0
        private const val TOUCH_MODE_DOWN = 1
        private const val TOUCH_MODE_DRAGGING = 2
    }

    private val vc = ViewConfiguration.get(v.context)
    private val mScaledTouchSlop = vc.scaledTouchSlop
    private var mTouchMode = 0
    private var mTouchX = 0f
    private var mTouchY = 0f

    /**
     * @return true if (x, y) is within the target area of the switch thumb
     * x,y and rect are in view coordinates, 0,0 is top left of the view
     */
    private fun hitThumb(x: Float, y: Float): Boolean {
        val rect = v.thumbDrawable.bounds
        return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
    }

    override fun onTouch(view: View, event: MotionEvent): Boolean {
        if (view == v) {
            when (MotionEventCompat.getActionMasked(event)) {
                MotionEvent.ACTION_DOWN -> {
                    val x = event.x
                    val y = event.y
                    if (v.isEnabled && hitThumb(x, y)) {
                        mTouchMode = TOUCH_MODE_DOWN;
                        mTouchX = x;
                        mTouchY = y;
                    }
                }
                MotionEvent.ACTION_MOVE -> {
                    val x = event.x
                    val y = event.y
                    if (mTouchMode == TOUCH_MODE_DOWN &&
                        (abs(x - mTouchX) > mScaledTouchSlop || abs(y - mTouchY) > mScaledTouchSlop)
                    )
                        mTouchMode = TOUCH_MODE_DRAGGING;
                }
                MotionEvent.ACTION_UP,
                MotionEvent.ACTION_CANCEL -> {
                    if (mTouchMode == TOUCH_MODE_DRAGGING) {
                        val r = v.thumbDrawable.bounds
                        if (r.left + r.right < v.width) lambda(false)
                        else lambda(true)
                    } else lambda(!v.isChecked)
                    mTouchMode = TOUCH_MODE_IDLE;
                }
            }
        }
        return v.onTouchEvent(event)
    }
}

如何使用:

实际触摸监听器,接受一个lambda表达式用于执行代码:

myswitch.setOnTouchListener(
    SwitchCompatTouchListener(myswitch) {
        // here goes all the code for your callback, in my case
        // i called a service which, when successful, in turn would 
        // update my liveData 
        viewModel.sendCommandToMyService(it) 
    }
)

为了完整起见,如果您有状态switchstate的观察者,它应该像这样:
switchstate.observe(this, Observer {
    myswitch.isChecked = it
})

Android团队真的应该修复这个问题...我所做的是在我的LiveData观察者中,注销onCheck,执行我的操作,然后将其设置回来。这只能工作因为UI更改发生在一个线程上(主线程)。 - Jeremy Jao

1

有两种方式,

  1. using xml onclick view Add Switch in XML as below:

    <Switch
    android:id="@+id/switch1"
    android:onClick="toggle"/>
    
在您的活动类中(例如MainActivity.java):
    Switch toggle; //outside oncreate
    toggle =(Switch) findViewById(R.id.switch1); // inside oncreate

    public void toggle(View view) //outside oncreate
    {
        if( toggle.isChecked() ){
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                start.setBackgroundColor(getColor(R.color.gold));
                stop.setBackgroundColor(getColor(R.color.white));
            }
        }
        else
        {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                stop.setBackgroundColor(getColor(R.color.gold));
                start.setBackgroundColor(getColor(R.color.white));
            }
        }
    }
  1. 使用点击监听器

在 XML 中添加开关,如下所示:

在 YourActivity 类中(例如 MainActivity.java),

    Switch toggle; // outside oncreate
    toggle =(Switch) findViewById(R.id.switch1);  // inside oncreate


    toggle.setOnClickListener(new View.OnClickListener() {   // inside oncreate
        @Override
        public void onClick(View view) {

            if( toggle.isChecked() ){
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    start.setBackgroundColor(getColor(R.color.gold));
                }
            }
            else
            {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    stop.setBackgroundColor(getColor(R.color.gold));
                }
            }

        }

    });

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