天天看点

View的Touch事件分析

文章目录

    • DecorView的Touch事件处理
    • Activity中的Touch事件处理函数
      • PhoneWindow对象superDispatchTouchEvent
    • View的Touch事件处理特性
    • View控件的Touch事件处理
      • View的onTouchEvent
      • ViewGroup控件的Touch事件处理

分析来自Android8.1.0源码

DecorView的Touch事件处理

我们现在先关注View相关的事件处理,先不关心如何到达View的。后面还会分析如何到达View。

View的Touch事件分析

我们先从DecorView的Touch事件处理开始

public class DecorView {
    private PhoneWindow mWindow;
	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
   		final Window.Callback cb = mWindow.getCallback();
    	return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
            	? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
	}
}
           
  1. 获取

    PhoneWindow

    对象的

    Callback

    对象。

    PhoneWindow

    是在

    Activity

    attach

    方法中创建的,并且

    Activity

    实现了

    Window.Callback

    ,并传递给

    PhoneWindow

    作为

    Callback

    的回调。
  2. 如果不

    cb

    null

    cb

    没有被销毁,

    mFeatureId

    小于0就会执行

    Window.Callback

    对象的

    dispatchTouchEvent

    方法。

    Activity

    运行时,前两个满足,当创建

    Activity

    的时候

    mFeatureId

    实际为-1。所以会调用

    Window.Callback

    对象的

    dispatchTouchEvent

    方法,也就是

    Activity

    实现的方法。

Activity中的Touch事件处理函数

有touch事件来的时候,会调用Activity的dispatchTouchEvent()派发事件,然后会调用getWindow().superDispatchTouchEvent(ev)派发事件,最终会调用布局中的View.

public class Activity extends ContextThemeWrapper
        implements Window.Callback {
	public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }
}
           
  1. 如果是

    MotionEvent.ACTION_DOWN

    会调用

    onUserInteraction

    接口
  2. 获取

    Window

    对象,实际为

    PhoneWindow

    对象。并调用其

    superDispatchTouchEvent

    方法,如果处理了直接返回

    true

  3. 如果

    Window

    对象调用

    superDispatchTouchEvent

    没有处理事件,就直接调用

    Activity

    onTouchEvent

    方法。

因为

Window

对象实际为

PhoneWindow

对象,我们继续分析一下

PhoneWindow

superDispatchTouchEvent

方法。

PhoneWindow对象superDispatchTouchEvent

public class PhoneWindow extends Window {
	@Override
	public boolean superDispatchTouchEvent(MotionEvent event) {
    	return mDecor.superDispatchTouchEvent(event);
	}
}
           

将会调用

DecorView

superDispatchTouchEvent

public class DecorView extends FrameLayout {
	public boolean superDispatchTouchEvent(MotionEvent event) {
    	return super.dispatchTouchEvent(event);
	}
}
           

最后会调用父类的

dispatchTouchEvent

方法

View的Touch事件分析

FrametLaout

继承

ViewGroup

ViewGroup

继承自

View

,并复写了View的

dispatchTouchEvent

方法。

总结:

Activity

Touch

会调用到

PhoneWindow

superDispatchTouchEvent

方法处理

Touch

事件,

PhoneWindow

又交由

DecorView

superDispatchTouchEvent

方法处理,

DecorView

会调用其父类的

dispatchTouchEvent

来处理,也就是

ViewGroup

dispatchTouchEvent

来处理。

下面我们在分析

ViewGroup

dispatchTouchEvent

之前我们先来分析一下

View

dispatchTouchEvent

处理

View的Touch事件处理特性

  • View

    中的

    Touch

    事件一个是

    View

    控件的

    Touch

    事件处理,一个是

    ViewGroup

    控件的

    Touch

    事件处理,二者处理方式有所不同
  • ViewGroup

    Touch

    事件处理是先由子

    View

    控件处理,子

    View

    控件没有消耗,然后有父View控件进行处理.
  • 对于

    ViewGroup

    控件可以通过

    onInterceptTouchEvent

    的方法拦截

    Touch

    事件
  • View

    中的

    Touch

    事件处理会先调用

    dispatchTouchEvent

    分发,然后调用

    onTouch

    处理,未处理会调用

    onTouchEvent

    处理。View控件还是ViewGroup控件处理逻辑都是一致

View控件的Touch事件处理

View

处理

Touch

事件时首先被调用的是

dispatchTouchEvent()

,此方法的主要工作是调用恰当的方法进行处理.

public class View{
	public boolean dispatchTouchEvent(MotionEvent event) {
        ......
        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        ......
    }
}
           

先调用

onFilterTouchEventForSecurity

方法是否需要过滤事件,我们先来看一下此方法。

public class View {
    public boolean onFilterTouchEventForSecurity(MotionEvent event) {
        //noinspection RedundantIfStatement
        if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
                && (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
            // Window is obscured, drop this touch.
            return false;
        }
        return true;
    }
}
           

检查

View

Window

是否被遮盖,如果被遮盖就过滤掉事件。

接着我们继续分析

dispatchTouchEvent

方法。

public class View{
	public boolean dispatchTouchEvent(MotionEvent event) {
        ......
        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            ......
        }
        ......
    }
}
           

接着检查

ENABLED_MASK

,也就是

View

检查是否

enable

,因为只有

enable

的状态下的

View

才能处理事件。接着调用

handleScrollBarDragging

方法,也就是处理滑动scroll bar事件,如果是,

result

true

我们继续分析。

public class View{
	public boolean dispatchTouchEvent(MotionEvent event) {
        ......
        if (onFilterTouchEventForSecurity(event)) {
        	......
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }
		   ......
        }
        ......
    }
}
           

ListenerInfo

中封装了各种

Listener

的接口对象,有

OnClickListener

OnLongClickListener

OnTouchListener

等等。这里是检测是否存在

OnTouchListener

接口对象,也就是我们是否通过

setOnTouchListener

设置了

OnTouchListener

,是否

OnTouchListener

onTouch

是否会处理此事件。如果处理

result

true

接着分析。

public class View{
	public boolean dispatchTouchEvent(MotionEvent event) {
        ......
        if (onFilterTouchEventForSecurity(event)) {
		   ·······
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        ......
    }
}
           

如果不是滑动scroll bar,并且没设置

OnTouchListener

或者设置了

OnTouchListener

onTouch

不处理此事件。那就会调用

View

onTouchEvent

方法处理。

总结:

View

先判断是否是scroll bar的滚动,然后是调用

OnTouchListener

onTouch

,二者皆没有处理就调用

View

onTouchEvent

方法进行处理

我们来分析

View

onTouchEvent

方法

View的onTouchEvent

View的onTouchEvent比较多我们来一段一段分析

public boolean onTouchEvent(MotionEvent event) {
    final float x = event.getX();
    final float y = event.getY();
    final int viewFlags = mViewFlags;
    final int action = event.getAction();

    final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
            || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
            || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
    ......
}
           

获取事件的作为

x

y

值。然后是检测

view

flag

是否存在

CLICKABLE

LONG_CLICKABLE

CONTEXT_CLICKABLE

。是否支持点击,长按点击,以及内容点击。

public boolean onTouchEvent(MotionEvent event) {
        ······
        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
            // A disabled view that is clickable still consumes the touch
            // events, it just doesn't respond to them.
            return clickable;
        }
        ······
    }
           

检测

View

flag

enable

的状态,如果是

DISABLED

,接着检测检测事件是否是

MotionEvent.ACTION_UP

,并且检测是否是PRESSED,如果是PRESSED,设置pressed的状态为false。

最后是返回

clickable

,说明如果

View

enable

DISABLED

但是设置相关的click,仍然会消耗此事件。

下面是关于Event的事件分析了。我们知道一次点按的顺序是按下,移动,抬起。因此我们也以这个顺序来分析。

以ACTION_DOWN事件分析

public boolean onTouchEvent(MotionEvent event) {
      ......
            switch (event.getAction()) {
			   ......
                case MotionEvent.ACTION_DOWN:
                    if (!clickable) {
                        checkForLongClick(0, x, y);
                        break;
                    }
                    if (isInScrollingContainer) {
                    } else {
                        setPressed(true, x, y);
                        checkForLongClick(0, x, y);
                    }
                    break;
            }
		   ......
            return true;
        }
        return false;
    }
           

把代码抽取关键部分,检测clickable,若果是false,直接调用checkForLongClick检测长按事件。

接着如果是在滚动的布局中执行相关的操作,这里我们不关心,直接查看不是在滚动布局中。调用

setPressed

设置

pressed

状态,

checkForLongClick

检测长按事件。

setPressed

主要是设置

PFLAG_PRESSED

这里不做分析了。

我们来看一下

checkForLongClick

如何检测长按事件的。

private void checkForLongClick(int delayOffset, float x, float y) {
        if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE || (mViewFlags & TOOLTIP) == TOOLTIP) {
            mHasPerformedLongPress = false;

            if (mPendingCheckForLongPress == null) {
                mPendingCheckForLongPress = new CheckForLongPress();
            }
            mPendingCheckForLongPress.setAnchor(x, y);
            mPendingCheckForLongPress.rememberWindowAttachCount();
            mPendingCheckForLongPress.rememberPressedState();
            postDelayed(mPendingCheckForLongPress,
                    ViewConfiguration.getLongPressTimeout() - delayOffset);
        }
    }
           

检测

LONG_CLICKABLE

是否支持长按,检测

TOOLTIP

是否长按显示提醒。

如果支持一种,就会创建一个

CheckForLongPress

对象。然后通过postDelayed发送一个延迟操作。默认的延迟操作为500ms。我们后面在分析触发长按的情况。

分析ACTION_MOVE

public boolean onTouchEvent(MotionEvent event) {
	    ...
            switch (event.getAction()) {
            ...
                case MotionEvent.ACTION_MOVE:
                    // Be lenient about moving outside of buttons
                    if (!pointInView(x, y, mTouchSlop)) {
                        // Outside button
                        removeTapCallback();
                        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                            // Remove any future long press/tap checks
                            removeLongPressCallback();
                            setPressed(false);
                        }
                    }
                    break;
			}
		}
    }
           

pointInView方法

是否判断坐标是否在View内,一旦移动出

View

范围,就会清除状态,移除长按消息,并设置

pressed

状态为

false

分析ACTION_UP

public boolean onTouchEvent(MotionEvent event) {
	    ...
            switch (action) {
                case MotionEvent.ACTION_UP:
                	
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    //如果PRESSED状态下执行
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        boolean focusTaken = false;
                        //请求焦点
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }
                        //如果长按没有触发,并且忽略下次的up事件为false执行
                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                            //移除长按的监听
                            removeLongPressCallback();

                            //仅仅获取焦点才执行
                            if (!focusTaken) {
                                //这段代码主要是执行调用OnClickListener的onClick,可以自行分析
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClick();
                                }
                            }
                        }
					  // 创建重置Pressed的Runnable对象
                        if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }
					  //运行Pressed的Runnable
                        if (prepressed) {
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }
                    }
                    break;
		}
           

上面的代码主要的功能是

  1. 获取焦点
  2. 长按事件没有执行,调用

    OnClickListener

    onClick

  3. 重置

    Pressed

    状态。

我们还遗留一个问题是长按事件的触发情况,我们来分析一下。

private final class CheckForLongPress implements Runnable {

    @Override
    public void run() {
        if ((mOriginalPressedState == isPressed()) && (mParent != null)
                && mOriginalWindowAttachCount == mWindowAttachCount) {
            if (performLongClick(mX, mY)) {
                mHasPerformedLongPress = true;
            }
        }
    }
}
           

判断是否是按下状态等等,最后执行View的

performLongClick

方法。

public boolean performLongClick(float x, float y) {
    mLongClickX = x;
    mLongClickY = y;
    final boolean handled = performLongClick();
    mLongClickX = Float.NaN;
    mLongClickY = Float.NaN;
    return handled;
}
           

然后调用View的重载的performLongClick方法。

public boolean performLongClick() {
    return performLongClickInternal(mLongClickX, mLongClickY);
}
           

然后调用到

View

performLongClickInternal

方法。

private boolean performLongClickInternal(float x, float y) {
    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);

    boolean handled = false;
    final ListenerInfo li = mListenerInfo;
    //处理事件
    if (li != null && li.mOnLongClickListener != null) {
        handled = li.mOnLongClickListener.onLongClick(View.this);
    }
    //处理contextMenu
    if (!handled) {
        final boolean isAnchored = !Float.isNaN(x) && !Float.isNaN(y);
        handled = isAnchored ? showContextMenu(x, y) : showContextMenu();
    }
    //处理长按时的tip
    if ((mViewFlags & TOOLTIP) == TOOLTIP) {
        if (!handled) {
            handled = showLongClickTooltip((int) x, (int) y);
        }
    }
    return handled;
}
           

从上面的代码可以知道了先调用

OnLongClickListener

onLongClick

方法,没有处理掉事件由contextMenu来处理,没有处理掉最后是长按时的tip。三者是互斥的。

从上面可以总结出click事件的触发的touch事件

  • 点击事件: ACTION_DOWN -> ACTION_UP或者ACTION_DOWN -> ACTION_MOVE…-> ACTION_UP(未移动到View之外) 小于500ms or 大于500ms长按没有被处理
  • 长按事件: ACTION_DOWN-> ACTION_MOVE…(未移动到View之外) 大于500ms,可以处理。

ViewGroup控件的Touch事件处理

ViewGroup

继承自

View

,覆写

View

dispatchTouchEvent()

的方法。我们从很多书上都说

ViewGroup

touch

事件处理,先派发事件处理给子

View

控件处理,子

View

事件处理后返回

false

就有

ViewGroup

处理,从源码角度分析。到底是如何实现的。

由于代码太多,分段阅读。

分析ACTION_DOWN事件

先分析事件为

ACTION_DOWN

时。

public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean handled = false;
        if (onFilterTouchEventForSecurity(ev)) {
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;
            //1. 初始化状态
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }
            .......
    }
           

也是调

onFilterTouchEventForSecurity

判断是否

Event

是否需要过滤。不需要过滤时,接着调用判断是

ACTION_DOWN

时,调用

cancelAndClearTouchTargets

resetTouchState

来清理原有状态,保证状态正确。

public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean handled = false;
        if (onFilterTouchEventForSecurity(ev)) {
	 	    // 2. 检测拦截
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                intercepted = true;
            }
        }
}
           

检测是否要拦截touch事件, 检测是否是

ACTION_DOWN

,是否

mFirstTouchTarget

(其实这个是处理事件的)为

null

,二者都不满足的话,拦截此事件,有其中一个满足就检测

FLAG_DISALLOW_INTERCEPT

,此

flag

设置,就说明不允许

ViewGroup

拦截事件,没设置最后会调用

onInterceptTouchEvent

来检测拦截。

public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean handled = false;
        if (onFilterTouchEventForSecurity(ev)) {
		   ......
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;
            TouchTarget newTouchTarget = null;
            boolean alreadyDispatchedToNewTouchTarget = false;
            //没有拦截,也没有取消
            if (!canceled && !intercepted) {
                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

                    final int childrenCount = mChildrenCount;
                    if (newTouchTarget == null && childrenCount != 0) {
                        final float x = ev.getX(actionIndex);
                        final float y = ev.getY(actionIndex);
                        //获取分发事件的子View的事件
                        final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        //遍历子View看哪个可以处理事件
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = getAndVerifyPreorderedIndex(
                                    childrenCount, i, customOrder);
                            final View child = getAndVerifyPreorderedView(
                                    preorderedList, children, childIndex);
                           // 先判断子View是否可以接受事件,接着判断是否在子View的区域内。不满足直接继续
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }
						  //如果已经有可以处理的target直接返回
                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }
						  //调用dispatchTransformedTouchEvent来判断子View能否处理事件
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                if (preorderedList != null) {
                                    for (int j = 0; j < childrenCount; j++) {
                                        if (children[childIndex] == mChildren[j]) {
                                            mLastTouchDownIndex = j;
                                            break;
                                        }
                                    }
                                } else {
                                    mLastTouchDownIndex = childIndex;
                                }
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                //设置mFirstTouchTarget
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }
                        }
                    }
                }
            }
        }
}
           

这部分主要查找

ViewGroup

的子View来处理事件。查找是通过

canViewReceivePointerEvents

(也就是通过判断可见性)和

isTransformedTouchPointInView

(通过touch事件是否在

View

上)函数。对于消息的处理,如果子

View

消耗了事件,会通过

addTouchTarget

函数设置

mFirstTouchTarget

(后面会直接通过它来处理

ACTION_UP

事件),如果没有子

View

处理事件,

mFirstTouchTarget

会为

null

接着分析

public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean handled = false;
        if (onFilterTouchEventForSecurity(ev)) {
		   ......
            //上面如果没有子View,然后mFirstTouchTarget为null,dispatchTransformedTouchEvent处理
            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;
                    //上面查找的时候已经处理,因此if满足,handled为true
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    } else {
                    }
                }
            }
        }
}
           

到此

ACTION_DOWN

分析完成了。唯一的是不管是子View还是ViewGroup本身都调用了

dispatchTransformedTouchEvent

处理事件,只是child参数不同,ViewGroup传递null,子View传递相应的对象。我们来分析一下此方法。

下面分析一下

dispatchTransformedTouchEvent

到底如何处理事件

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        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;
        }
     ......
 }
           

此段判断是否为是否为cancel,是cancel,如果child不为null,就调用child的dispatchTouchEvent,否则就调用ViewGroup的继承类View的事件处理。

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
     if (newPointerIdBits == oldPointerIdBits) {
            if (child == null || child.hasIdentityMatrix()) {
                if (child == null) {
                    handled = super.dispatchTouchEvent(event);
                } else {
                    final float offsetX = mScrollX - child.mLeft;
                    final float offsetY = mScrollY - child.mTop;
                    event.offsetLocation(offsetX, offsetY);

                    handled = child.dispatchTouchEvent(event);

                    event.offsetLocation(-offsetX, -offsetY);
                }
                return handled;
            }
            transformedEvent = MotionEvent.obtain(event);
        } else {
            transformedEvent = event.split(newPointerIdBits);
        }
 }
           

判断上次事件的点与现在事件的点就是相同,处理的方式也是相同。

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
     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);
      }
}
           

处理方式也是相同。

分析ACTION_MOVE

public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean handled = false;
        if (onFilterTouchEventForSecurity(ev)) {
		   ......
            if (mFirstTouchTarget == null) {
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                while (target != null) {
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    } else {
			           handled = child.dispatchTouchEvent(transformedEvent);
                    }
                }
            }
        }
}
           

对于

ACTION_MOVE

的事件拦截的检测跟

ACTION_DOWN

一致。如果

ACTION_DOWN

没有子View处理,

mFirstTouchTarget

null

ACTION_MOVE

也是没有子

View

处理的,调用

dispatchTransformedTouchEvent

处理,如果有子

View

mFirstTouchTarget

不为

null

,而且

ACTION_MOVE

还没处理,调用子

View

dispatchTouchEvent

处理。

ACTION_UP

处理的过程与

ACTION_MOVE

一致。