Android6.0系统View事件分发

上篇文章《快速了解Android6.0系统触摸事件工作原理——InputManagerService》分析到dispatchTouchEvent()方法来做分发事件的处理,这个分发过程分为两种情况:(1)若目前的View是普通的View,就会调用View.java的dispatchTouchEvent()方法来处理。普通的View,比如TextView里面并没有实现dispatchTouchEvent()方法,由于它又是继承View,所有在View的dispatchPointerEvent()方法中调用dispatchTouchEvent()方法或者进入View的dispatchTouchEvent()方法。(2)若目前的View是容器类View,比如DecorView,由于它实现了dispatchTouchEvent()方法,所以View的dispatchPointerEvent()方法中调用dispatchTouchEvent()方法就会进入DecorView的dispatchTouchEvent()方法。

1、普通View事件分发

/frameworks/base/core/java/android/view/view.java

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;
            }
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
       ...
    }

View的dispatchTouchEvent()方法先判断当前是不是有mOnTouchListener,如有就调用它的onTouch()方法,若没有就会到if中调用onTouchEvent()方法,如若子类没有去实现onTouchEvent()方法,这个调用就进入View的onTouchEvent()方法中
/frameworks/base/core/java/android/view/view.java

public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;
        final int action = event.getAction();
        //首先得到event的坐标x,y的值
        ...
        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
                (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
            switch (action) {
                case MotionEvent.ACTION_UP: //若action为抬起
                    ..
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                      ...
                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                            ...
                            if (!focusTaken) {
                                ...
                                if (!post(mPerformClick)) {
                                    performClick();
                                }
                            }
                        }
                       ...
                    break;
                case MotionEvent.ACTION_DOWN:  //按下动作
                    ..
                    break;
                case MotionEvent.ACTION_CANCEL: //取消动作
                    ...
                    break;
                case MotionEvent.ACTION_MOVE: //Move动作
                    ...
                    break;
            }
            return true;
        }
        return false;
    }

View的onTouchEvent()方法返回值,返回的true,表示已经处理event;返回的false,表示没有处理,在方法中真正实现的是对UP、Down、Move事件的处理。以UP事件为例,分析这个case经过多个if判断最后调用performClick()方法的现象。
/frameworks/base/core/java/android/view/view.java

public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
 }

performClick()方法中的变量mOnClickListener是一个OnClickListener对象,在应用程序开发中的View,比如Button,添加这个按钮事件时,代码如下:

Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener(){
     public void onClick(View v){
          //做具体的工作内容
     }
});

上面代码new一个OnClickListener对象并且通过setOnClickListener()方法设置到View中,所以performClick()方法中的mOnClickListener就从应用程序开发中设置。在View的performClick()方法中,调用了 li.mOnClickListener中的onClick(this)方法,这样就进入到应用程序开发中的onClick()方法,就如上面的button例子,最后进入到了button的onClick()方法中。

2、容器类View事件分发

以View是DecorView为例,View中的dispatchPointerEvent()方法调用dispatchTouchEvent()方法之后会进入DecorView的dispatchTouchEvent()方法。
/frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

public class PhoneWindow extends Window implements MenuBuilder.Callback {
...
    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
        ...
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            final Callback cb = getCallback();
            return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev): super.dispatchTouchEvent(ev);
        }
        ...
    }
}

DecorView类中的dispatchTouchEvent()方法通过调用getCallback()方法返回Window.Callback接口对象,Activity的attach()方法会把Activity的callback设置到window中,因此getCallback()方法获得目前的Activity对象。isDestroyed()方法用于判断Activity是否destroy,mFeatureId < 0用于判断目前的view是否是根视图DecorView。因为目前case是根视图对象,所以cb != null && !isDestroyed() && mFeatureId < 0为true,于是调用cb.dispatchTouchEvent()方法进入到Activity中。
/frameworks/base/core/java/com/android/app/Activity.java

public boolean dispatchTouchEvent(MotionEvent ev) {
      if (ev.getAction() == MotionEvent.ACTION_DOWN) {
           onUserInteraction();
       }
       if (getWindow().superDispatchTouchEvent(ev)) {
           return true;
       }
       return onTouchEvent(ev);
}

Activity的dispatchTouchEvent()方法首先判断Action是否是Down,若是,就调用onUserInteraction()方法,接着通过getWindow()方法获得PhoneWindow对象,并调用它的superDispatchTouchEvent()方法,返回true,表示目前已找到View来处理事件,就不需要Activity来处理事件了,也不调用Activity的onTouchEvent()方法,否则,getWindow().superDispatchTouchEvent(ev)返回false,表示需要Activity来处理,此时需要调用它的onTouchEvent()方法来进一步处理事件。
/frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

public class PhoneWindow extends Window implements MenuBuilder.Callback {
   ...
   public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }
    ...
    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
        public boolean superDispatchTouchEvent(MotionEvent event) {
            return super.dispatchTouchEvent(event);
        }
        ...
    }
    ...
}

变量mDecor是PhoneWindow中的一个DecorView类型的全局变量,在PhoneWindow中的superDispatchTouchEvent()方法中会调用它的superDispatchTouchEvent()方法。而superDispatchTouchEvent()方法又直接调用其父类型的dispatchTouchEvent()方法来就一步操作。DecorView类继承了FrameLayout类,并且FrameLayout类并没有实现dispatchTouchEvent()方法,实现这个方法是容器类ViewGroup的任务。由于FrameLayout类继承ViewGroup类,因此就调用了ViewGroup类的dispatchTouchEvent()方法。
/frameworks/base/core/java/android/view/ViewGroup.java

public boolean dispatchTouchEvent(MotionEvent ev) {
       ...
        if (onFilterTouchEventForSecurity(ev)) {
            //判断这个event是否符合安全策略
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            if (actionMasked == MotionEvent.ACTION_DOWN) { //清除以往的Touch状态
                cancelAndClearTouchTargets(ev); //把mFirstTouchTarget设置为null
                resetTouchState(); //充值Touch状态标识
            }

            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); 
                } else {
                    intercepted = false;
                }
            } else {
                intercepted = true;
            }

            ...
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;
            ...
            if (!canceled && !intercepted) {
                ...
                //处理ACTION_DOWN事件
                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                    ...
                    final int childrenCount = mChildrenCount;
                    //得到子View的个数
                    if (newTouchTarget == null && childrenCount != 0) {
                        //根据Touch坐标找出子View来接收Touch事件
                        final float x = ev.getX(actionIndex);
                        final float y = ev.getY(actionIndex);
                        ...
                        //遍历子View查找子View接收Touch事件
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = customOrder
                                    ? getChildDrawingOrder(childrenCount, i) : i;
                            final View child = (preorderedList == null)
                                    ? children[childIndex] : preorderedList.get(childIndex);
                            ...
                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) { //接收Touch事件的子View已经被找到
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;  //跳出循环
                            }
                            resetCancelNextUpFlag(child);
                            //若不满足上面的if,就进入这个if,将Touch事件传递给子View做递归处理,即遍历该子View的所有子View
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                ...
                           }
       ...                 
    }

ViewGroup的dispatchTouchEvent()方法调用了dispatchTransformedTouchEvent()方法将事件分发到子View中。
/frameworks/base/core/java/android/view/ViewGroup.java

扫描二维码关注公众号,回复: 2182396 查看本文章
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);
        }
     ...
    }

在上面代码中,若变量child为空,就调用其父类的dispatchTouchEvent()方法来处理,若变量child不为空,就调用其dispatchTouchEvent()方法处理。由于child是View对象,因此这个调用又回到前面View分发事件的起始处dispatchTouchEvent()方法。依次类推,当child是一个ViewGroup,就继续这样分发下去,当child是一个普通的View,就调用View的dispatchTouchEvent()方法来处理。

总结

在View的dispatchPointerEvent()方法中,如果View是普通的View,即View没有实现dispatchTouchEvent()方法,那么分发时就调用它的dispatchTouchEvent()方法来处理;如果View是ViewGroup或者ViewGroup的子类,它们实现了dispatchTouchEvent()方法,分发时就调用它们的dispatchTouchEvent()方法来处理。

猜你喜欢

转载自blog.csdn.net/warticles/article/details/81054225