/**
* 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
})
7条答案
按热度按时间mzmfm0qo1#
试试这个!
px9o7tmv2#
您只需执行以下操作(setOnTouchListener是不必要的):
puruo6ea3#
带有Butterknife开关兼容状态更改
waxmsbnn4#
现在可以在同一个回调中使用isPressed方法进行检查。
c86crjj05#
我有同样的问题Slay解释的评论接受的答案。这里的解决方案!
我的解决方案,使用
SwitchCompat
和Kotlin。在我的情况下,只有当用户通过UI触发更改时,我才需要对更改作出React。事实上,我的开关对LiveData
作出React,这使得setOnClickListener
和setOnCheckedChangeListener
都不可用。setOnClickListener
实际上对用户交互作出正确React,但如果用户拖动拇指越过开关,则不会触发它。如果以编程方式切换开关,则另一端的setOnCheckedChangeListener
也会触发(例如通过观察者)。现在在我的情况下开关存在于两个片段上,等等,RestoreInstanceState在某些情况下将触发具有覆盖正确值的旧值的开关。因此,我查看了SwitchCompat的代码,并能够成功地模仿它在区分点击和拖动方面的行为,并使用它来构建一个自定义的touchlistener,它应该工作。
使用方法:
实际的触摸监听器,它接受lambda和要执行的代码:
为了完整起见,下面是状态
switchstate
(如果有)的观测器:vx6bjr1n6#
实际上,你只需要这个
就这样!
vi4fp9gy7#
在Kotlin,你要做的就是:
这是因为,即使''方法需要
OnCheckedChangeListener
接口的示例,在Kotlin中,当一个函数接收到一个只有一个函数作为参数的接口时,你可以表示给定的函数。当然,你可以删除显式lambda参数类型:
此外,如果您只想使用isChecked值,则可以将buttonView替换为_ like,如下所示: