Giải pháp của tôi, sử dụng một SwitchCompat
và Kotlin. Trong tình huống của tôi, tôi chỉ cần phản ứng với thay đổi nếu người dùng kích hoạt nó thông qua UI. Trong thực tế, công tắc của tôi phản ứng với a LiveData
, và điều này làm cho cả hai setOnClickListener
và setOnCheckedChangeListener
không thể sử dụng được. setOnClickListener
thực tế phản ứng chính xác với tương tác của người dùng, nhưng nó không được kích hoạt nếu người dùng kéo ngón tay cái qua công tắc.setOnCheckedChangeListener
ở đầu bên kia cũng được kích hoạt nếu công tắc được bật theo chương trình (ví dụ bởi người quan sát). Bây giờ trong trường hợp của tôi, công tắc đã có mặt trên hai mảnh và do đó, onRestoreInstanceState sẽ kích hoạt trong một số trường hợp công tắc có giá trị cũ ghi đè giá trị chính xác.
Vì vậy, tôi đã xem mã của SwitchCompat và có thể bắt chước thành công hành vi của nó trong việc phân biệt nhấp và kéo và sử dụng nó để xây dựng một trình nghe cảm ứng tùy chỉnh hoạt động như bình thường. Ở đây chúng tôi đi:
/**
* 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)
}
}
Làm thế nào để sử dụng nó:
trình nghe cảm ứng thực tế chấp nhận lambda với mã để thực thi:
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)
}
)
Để hoàn thiện, đây là cách người quan sát trạng thái switchstate
(nếu bạn có) trông giống như:
switchstate.observe(this, Observer {
myswitch.isChecked = it
})