Solusi saya, menggunakan a SwitchCompatdan Kotlin. Dalam situasi saya, saya perlu bereaksi terhadap perubahan hanya jika pengguna memicu melalui UI. Bahkan, saklar saya bereaksi terhadap LiveData, dan ini membuat keduanya setOnClickListenerdan setOnCheckedChangeListenertidak dapat digunakan. setOnClickListenersebenarnya bereaksi dengan benar terhadap interaksi pengguna, tetapi itu tidak dipicu jika pengguna menyeret ibu jari melintasi sakelar.setOnCheckedChangeListenerdi ujung yang lain dipicu juga jika sakelar dimatikan secara terprogram (misalnya oleh pengamat). Sekarang dalam kasus saya saklar hadir pada dua fragmen, dan onRestoreInstanceState akan memicu dalam beberapa kasus saklar dengan nilai lama menimpa nilai yang benar.
Jadi, saya melihat kode SwitchCompat, dan mampu meniru perilakunya dengan sukses dalam membedakan klik dan seret dan menggunakannya untuk membangun daftar sentuh kustom yang berfungsi sebagaimana mestinya. Kita mulai:
/**
* 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)
}
}
Bagaimana cara menggunakannya:
pendengar sentuh aktual yang menerima lambda dengan kode untuk dieksekusi:
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)
}
)
Demi kelengkapan, beginilah tampilan pengamat untuk negara switchstate(jika Anda memilikinya):
switchstate.observe(this, Observer {
myswitch.isChecked = it
})