android事件处理流程

       最近在做一个效果,viewgroup要响应滑动事件,它的一个子view还要响应点击事件。为了实现该效果,不得不重新研究Android 事件处理流程。

       理解下面三个方法的左右对实现该效果有很大的帮助,

public boolean dispatchTouchEvent(MotionEvent event)
public boolean onTouchEvent(MotionEvent event) 
public boolean onInterceptTouchEvent(MotionEvent event)

       其实View中没有onInterceptTouchEvent方法。

   至于这三个方法的作用,我大致都比较清楚。只是对于不同的返回值代表的效果,需要做测试。

   经过测试,dispatchTouchEvent方法在返回true或者false的时候,都不会向下传递事件,也就是说子view的dispatchTouchEvent方法不会被调用。这两者不同的是,true表示DOWN事件后续的事件会继续接收,而false则不会接收。当返回super.dispatchTouchEvent的时候,也就是调用ViewGroup的dispatchTouchEvent方法,则会调用子view的dispatchTouchEvent方法。在ViewGroup的dispatchTouchEvent中会调用onInterceptTouchEvent方法,该方法表示是否拦截事件。在ViewGroup中的实现很简单,如下:

 

 public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }

  表示默认是不拦截的。

  如果不拦截,在ViewGroup的dispatchTouchEvent方法中会调用每个子view的dispatchTouchEvent方法,关键代码如下:

 

 for (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = customOrder ?
                                    getChildDrawingOrder(childrenCount, i) : i;
                            final View child = children[childIndex];
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                continue;
                            }

                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                                // Child is already receiving touch within its bounds.
                                // Give it the new pointer in addition to the ones it is handling.
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }

                            resetCancelNextUpFlag(child);
//在这里调用每个子view的dispatchTransformedTouchEvent方法

 

                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
                                mLastTouchDownTime = ev.getDownTime();
                                mLastTouchDownIndex = childIndex;
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }
                        }

 

  private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;

        // Canceling motions is a special case.  We don't need to perform any transformations
        // or filtering.  The important part is the action, not the contents.
        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;
        }

 

       其中child.dispatchTouchEvent(event)这句在执行的时候如果是该child是Viewgroup会继续递归调用上述viewgroup中的dispatchTouchEvent方法。如果child是一个View的话,会调用View中的dispatchTouchEvent方法,该方法实现如下:

/**
 * 返回true表示该事件被消化,否则返回false
 */ 
public boolean dispatchTouchEvent(MotionEvent event) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        if (onFilterTouchEventForSecurity(event)) {
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                return true;
            }

            if (onTouchEvent(event)) {
                return true;
            }
        }

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }
        return false;
    }
 在下面这个判断中,会调用View的onTouchListener的onTouch方法。
 if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                return true;
            }
           从上面分析中可以看出事件处理的大致流程。在 dispatchTouchEvent方法中会调用到 onInterceptTouchEvent方法来决定是否调用子View的 dispatchTouchEvent方法和onTouchEvent方法。

 

 

猜你喜欢

转载自zhaoxin1943.iteye.com/blog/2174763