因为项目需要,点击的时候,根据相应的条件判断,如果条件成立,滑块滚动,否则不滚动。然后网上查了一圈,没有什么好的办法,只能看源码,解决这个问题。思路是这样的,我找了一下那个check的改变位置,找到两处,一个是toggle方法,另外一个是setCheck方法。我们知道setCheck方法是我们可以自己设置,这个显示不是点击的时候,做的事,然后我就跟踪toggle方法,看谁调用了它。这个是switchcompact的toggle方法源码
public void toggle() {
this.setChecked(!this.isChecked());
}
那谁调用了呢?SwitchCompact没有调用,应该是父类调用了这个方法,父类是谁呢CompoundButton,它也有toggle方法,
@Override
public void toggle() {
setChecked(!mChecked);
}
发现该类的performClick方法调用,这个不是点击事件嘛?
@Override
public boolean performClick() {
toggle();
final boolean handled = super.performClick();
//这个方法调用,onclicklistener.onClick此才会调用,此时toggle已经调用了,
//所以onClick里面进行事件间隔进行拦截的方案不可行
if (!handled) {
// View only makes a sound effect if the onClickListener was
// called, so we'll need to make one here instead.
playSoundEffect(SoundEffectConstants.CLICK);
}
return handled;
}
其实performclick这个方法,熟悉事件分发机制的同学应该知道,是我们处理处理点击事件的时候
//View.java 方法以及代码片段
private boolean performClickInternal() {
// Must notify autofill manager before performing the click actions to avoid scenarios where
// the app has a click listener that changes the state of views the autofill service might
// be interested on.
notifyAutofillManagerOnClick();
return performClick();
}
public boolean performClick() {
// We still need to call this method to handle the cases where performClick() was called
// externally, instead of through performClickInternal()
notifyAutofillManagerOnClick();
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
//平时setOnclicklistener的业务调用位置,所以在onlcicklistener里面进行时间间隔判断,这个时候checked已经调用了
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
//点击事件执行Runable
private final class PerformClick implements Runnable {
@Override
public void run() {
recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);
performClickInternal();
}
}
而Runbale的执行则在onTouchEvent里面post执行的,由于代码太多,则截取片段
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
}
而更改的逻辑在toggle里面,所以我们重写perfomClick或者toggle都可以拦截对状态的更改。
class CustomSwitch : SwitchCompat {
private var action: (() -> Boolean)? = null
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
var canClick: Boolean = false
private var lastClickTime: Long = 0
fun setClickAction(clickAction: () -> Boolean) {
action = clickAction
}
override fun toggle() {
//两次点击间隔超过300ms才给执行performClick时间
val currentTime = System.currentTimeMillis()
if (Math.abs(currentTime - lastClickTime) > 300) {
lastClickTime = currentTime
if (action == null) {
super.toggle()
} else {
action?.invoke()?.apply {
if (this && !canClick) {
canClick = true
super.toggle()
}
}
}
} else {
lastClickTime = currentTime
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
action = null
}
override fun onTouchEvent(ev: MotionEvent?): Boolean {
//过滤move事件
if (ev?.action == MotionEvent.ACTION_MOVE) return false
return super.onTouchEvent(ev)
}
}
对于该类可以继续扩展,比如滑动啊,我这里做的处理是禁止滑动,你也可以根据自己的情况,让它滑动。