我的解决方案是使用
SwitchCompat
和 Kotlin。在我的情况下,我只需要在用户通过 UI 触发更改时才对其进行反应。实际上,我的开关会对一个
LiveData
进行反应,这使得
setOnClickListener
和
setOnCheckedChangeListener
都无法使用。实际上,
setOnClickListener
对用户交互做出了正确的反应,但如果用户拖动开关,它就不会被触发。另一方面,
setOnCheckedChangeListener
也会在程序切换开关时触发(例如由观察者触发)。现在,在我的情况下,该开关存在于两个片段中,因此在某些情况下,onRestoreInstanceState 会触发带有旧值的开关,从而覆盖了正确的值。
所以,我查看了 SwitchCompat 的代码,并成功地模仿了它区分单击和拖动的行为,并使用它来构建一个自定义的触摸侦听器,使其正常工作。以下是代码:
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
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) {
viewModel.sendCommandToMyService(it)
}
)
为了完整起见,如果您有状态
switchstate
的观察者,它应该像这样:
switchstate.observe(this, Observer {
myswitch.isChecked = it
})