在Android中Touch触摸事件主要包括点击(onClick)、长按(onLongClick)、拖拽(onDrag)、滑动(onScroll)等

  • 按下(ACTION_DOWN)
  • 移动(ACTION_MOVE)
  • 抬起(ACTION_UP)
  • 取消手势(ACTION_CANCEL)
  • 滑出屏幕(ACTION_OUTSIDE)

开始

当屏幕被触摸,Linux内核会将硬件产生的触摸事件包装为Event存到/dev/input/event[x]目录下

接着,系统创建的一个InputReaderThread线程loop起来让EventHub调用getEvent()不断的从/dev/input/文件夹下读取输入事件

然后InputReader则从EventHub中获得事件交给InputDispatch。

而InputDispatch又会把事件分发到需要的地方,比如ViewRootImpl的WindowInputEventReceiver中。

然后就调用了ViewRootImpl.enqueueInputEvent()

一波三折后,最终调用了DecorView.dispatchPointerEvent

1
2
3
4
5
6
7
private int processPointerEvent(QueuedInputEvent q) {
            ...
            // mView是decorview
            boolean handled = mView.dispatchPointerEvent(event);
            ...
            return handled ? FINISH_HANDLED : FORWARD;
        }

然后在调用了Activity的dispatchTouchEvent(ev),这就是一开始的大致流程

1
2
3
4
5
6
7
8
9
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    // 这里的window 就是 phoneWindow , callback 就是对应 activity
    // 也就是最终调用了 Window.cb.dispatchTouchEvent(ev)
    // 也就是activity的dispatchTouchEvent
    final Window.Callback cb = mWindow.getCallback();
    return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
            ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}

Actvity

dispatchTouchEvent方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    // 这个方法,基本上每次有触摸或者键盘事件的时候,都会触发该方法;
    // 目前是个空方法
        onUserInteraction();
    }
    // 这里就会通过PhoneWindow调用了Decorview.superDispatchTouchEvent(event)
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    // 这样就我们经常说的,如果View返回true的话
    // 就会调用activity的onTouchEvent(ev)方法
    return onTouchEvent(ev);
}

ViewGroup

dispatchTouchEvent(event)方法十分长,这里只保留重要的分析

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {

...
    boolean handled = false; // 开始是默认不处理 false 
    //// 是否过滤掉该事件 , 也就是判断这个事件的点,有没有超出屏幕或者这个窗口被遮住了 
    if (onFilterTouchEventForSecurity(ev)) { 
        // 记录 action , actionMask
        final int action = ev.getAction();
        final int actionMasked = action & MotionEvent.ACTION_MASK;

        // 如果是按下操作
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // 把之前的一些状态,重置,
            // 如果之前mFirstTouchTarget不是空,发送cancel事件出去
            // 重置之前的mFirstTouchTarget为空
            cancelAndClearTouchTargets(ev);
            resetTouchState();
        }

        // ViewGroup才有的,是否拦截事件
        final boolean intercepted;

        // 如果事件类型ACTION_DOWN或者mFirstTouchTarget不为空 才有机会触发onInterceptTouchEvent方法
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
            // disallowIntercept 就是我们是否跳过这次的拦截事件 
            // 子类可以调用equestDisallowInterceptTouchEvent 为方法true跳过拦截
            //但是对DOWN无效,因为DOWN会在开始重置状态
            // 如果是true,那这次的拦截事件不用管,
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            if (!disallowIntercept) {
                // false 走正常流程
                // onInterceptTouchEvent(ev) 是否拦截
                intercepted = onInterceptTouchEvent(ev);
                // 因为有可能,会拦截该事件,所以要重新setAction
                ev.setAction(action); 
            } else { 
                //disallowIntercept 是true ,跳过拦截事件,所以没有拦截
                intercepted = false;
            }
        } else {
            // 其他的情况都要拦截
            intercepted = true;
        }
        // 如果是onInterceptTouchEvent为true引起的拦截,取消target的焦点
        if (intercepted || mFirstTouchTarget != null) {
            ev.setTargetAccessibilityFocus(false);
        }

        // 判断是否cancel,一个是事件,一个是View的flag
        final boolean canceled = resetCancelNextUpFlag(this)
                || actionMasked == MotionEvent.ACTION_CANCEL;

        // 如果不取消,也不拦截
        if (!canceled && !intercepted) {
            // 如果是DOWN相关
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                final int actionIndex = ev.getActionIndex(); // always 0 for down
                final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                        : TouchTarget.ALL_POINTER_IDS;
                ...    找到接收Touch事件的子View
                        newTouchTarget = getTouchTarget(child);
                        if (newTouchTarget != null) {
                            // 如果不是空,跳出循环,说名找到了
                            newTouchTarget.pointerIdBits |= idBitsToAssign;
                            break;
                        }

                        resetCancelNextUpFlag(child);
                        // 否则通过dispatchTransformedTouchEvent方法找
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                        ... 最后把addTouchTarget添加child进去,mFirstTouchTarget不为空
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            // 此时alreadyDispatchedToNewTouchTarget为true说明已经分发过事件
                            alreadyDispatchedToNewTouchTarget = true;
                            break;
                        }
                    ...
        }

        // 如果mFirstTouchTarget还是空,说明没有子VIew返回true,那自己处理
        if (mFirstTouchTarget == null) {
            // No touch targets so treat this as an ordinary view.
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
        } else {
            ...
            while (target != null) {
                final TouchTarget next = target.next;
                // 这个基本是对应DOWN 分化过的,直接返回true
                if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                    handled = true;
                } else { 
                    // 可能是是拦截
                    final boolean cancelChild = resetCancelNextUpFlag(target.child)
                            || intercepted;
                    // 否则一个一个的分发下去        
                    if (dispatchTransformedTouchEvent(ev, cancelChild,
                            target.child, target.pointerIdBits)) {
                        handled = true;
                   ...
    return handled;
}

进入dispatchTransformedTouchEvent方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;

        // 如果是取消,发送cancel事件
        final int oldAction = event.getAction();
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }
        ...
        if (child == null) {
            //  如果是空,则调用父类
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
            final float offsetX = mScrollX - child.mLeft;
            final float offsetY = mScrollY - child.mTop;
            transformedEvent.offsetLocation(offsetX, offsetY);
            if (! child.hasIdentityMatrix()) {
                transformedEvent.transform(child.getInverseMatrix());
            }
            // 否则调用本身
            handled = child.dispatchTouchEvent(transformedEvent);
        }

        // Done.
        transformedEvent.recycle();
        return handled;
    }

View

最后到View中的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public boolean dispatchTouchEvent(MotionEvent event) {
        ... 
        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // 如果是按下,停止滑动
            stopNestedScroll();
        }
        // 重写onFilterTouchEventForSecurity方法可以跳过touch事件
        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            // 这里就是如果设置 OnTouchListener 的方法的话,也返回了true,就会跳过onTouchEvent方法
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }
            // 最后调用onTouchEvent方法
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        // 松手或者取消时候 停止滑动
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

onTouchEvent

主要是针对按下,抬起,等一些列判断,认为是点击,还是长按等操作