飞道的博客

干货总结!从源码分析点击事件、触摸事件、enabled、clickable的关系

500人阅读  评论(0)

直接来总结

附上本人总结的表格(已用代码验证)

enabled clickable onTouchListener是否会被调用 onTouchListener#onTouch返回值 onClickListener是否会被调用 onTouchEvent返回值 dispatchTouchEvent返回值
true true true true true
true true false true true
true false true true true
true false false false false
false true - true true
false false - false false
  • 只要viewenabledonTouchListener就会被调用
  • 只有viewenabled且可点击(clickablelongClickablecontextClickable任意一种),且onTouchListener返回falseonClickListener才会被调用
  • 如果onTouchListener没有消耗事件,且是可点击的(三种情况),那么无论设不设置onClickListener也无论其返回值是什么,此事件都会被消耗,因为ViewonTouchEvent对事件进行了默认处理

不设置onTouchListeneronClickListener相当于返回false
dispatchTouchEvent返回值是为了分析父子View事件分发,表示触摸事件是否被消费

分析

触摸事件(TouchEvent)分发的起始点为View#dispatchTouchEvent(MotionEvent event),上一层就是Native层了

onTouchListener与onTouchEvent以及enable的影响

View#dispatchTouchEvent(MotionEvent event)中有如下代码

//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
        && (mViewFlags & ENABLED_MASK) == ENABLED
        && li.mOnTouchListener.onTouch(this, event)) {
    //如果view是enabled,才会调用mOnTouchListener
    result = true;
}
 //如果调用mOnTouchListener的onTouch返回true,onTouchEvent不会被调用
if (!result && onTouchEvent(event)) {
    //如果view不是enabled或者mOnTouchListener的onTouch返回false,onTouchEvent被调用
    result = true;
}

小结

onTouchEvent作为兜底的触摸事件处理操作,无论view是不是enabled,此处都会调用,
但~~ 是,如果有mOnTouchListener并且其onTouch返回true 则表示该触摸事件已经被处理完了,则不会调用onTouchEvent

onTouchEvent与onClickListener以及clickable的影响

View#onTouchEvent(MotionEvent event)中有如下代码

//clickable、longClickable、contenxtClickable都视为可点击
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
        || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
        || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
if ((viewFlags & ENABLED_MASK) == DISABLED) {
    ......
    // 如果view不是enabled的
    // 如果是可点击的(上述三种情况),那么返回true,表示该事件被消费,但是不会真的响应它(不会调用onClickListener);
    //如果不可点击,那么返回false,表示该事件没有被消费
    return clickable;
}
......
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
    switch (action) {
        case MotionEvent.ACTION_UP:
            //当事件为UP时才是点击
            mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
            if ((viewFlags & TOOLTIP) == TOOLTIP) {
                handleTooltipUp();
            }
            //如果view不可点击(三种情况),那么就会直接返回false
            if (!clickable) {
                removeTapCallback();
                removeLongPressCallback();
                mInContextButtonPress = false;
                mHasPerformedLongPress = false;
                mIgnoreNextUpEvent = false;
                break;
            }
            //如果view是clickable执行到这里
            boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
            if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                ......
                if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                    ......
                    if (!focusTaken) {
                        //使用一个Runnable的对象post到队列中来执行点击事件,其内部就是执行performClickInternal()方法
                        //这是为了在单击操作之前,视图的其他视觉状态会更新(点击效果)
                        if (mPerformClick == null) {
                            mPerformClick = new PerformClick();
                        }
                        if (!post(mPerformClick)) {
                            performClickInternal();
                        }
                    }
                }
                ......
            }
            mIgnoreNextUpEvent = false;
            break;
        case MotionEvent.ACTION_DOWN:
            ......
            break;
        case MotionEvent.ACTION_CANCEL:
            ......
            break;
        case MotionEvent.ACTION_MOVE:
            ......
            break;
    }
    return true;
}
return false;

View#performClickInternal()又调用了View#performClick()方法,其部分代码如下

final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
    playSoundEffect(SoundEffectConstants.CLICK);
    //如果mOnClickListener存在就调用其onClick方法,返回true,表示点击事件被消费
    //但是这里的返回几乎没啥用,没有人使用它的返回值
    li.mOnClickListener.onClick(this);
    result = true;
} else {
    result = false;
}

小结

  • 如果view不是enabled的,那么不会调用onClickListenerview是否可点击(clickablelongClickablecontextClickable仅仅作为返回结果表示触摸事件是否被消费
  • 如果viewenabled
    • 如果view可点击,那就会调用其onClickListener,返回true表示触摸事件被点击监听器消费了
    • 如果view不可点击,直接返回false,表示触摸事件没有被消费

如果纰漏,欢迎指正


转载:https://blog.csdn.net/jankingmeaning/article/details/105473318
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场