三个重要的方法:

  • dispatchTouchEvent(MotionEvent ev):用来进行事件的分发
  • onInterceptTouchEvent(MotionEvent ev):用来进行事件的拦截,在dispatchTouchEvent()中调用,需要注意的是View没有提供该方法
  • onTouchEvent(MotionEvent ev):用来处理点击事件,在dispatchTouchEvent()方法中进行调用

语言描述:

     当父组件接收到点击事件后,进行事件分发,自己先拦截到事件,如果自己可以处理此事件,则自己消费事件;若无法消费事件,则交给子组件来处理。

源代码分析:

     ViewGroup中的dispatchTouchEvent(MotionEvent ev)入手

  public boolean dispatchTouchEvent(MotionEvent ev) {          //..省略        // Check for interception.        final boolean intercepted;        if (actionMasked == MotionEvent.ACTION_DOWN                || mFirstTouchTarget != null) {            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;            if (!disallowIntercept) {                //拦截事件,ViewGroup的onInterceptTouchEvent直接返回了false,不拦截事件                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;        }        //..省略        final View[] children = mChildren;//遍历所有子组件        for (int i = childrenCount - 1; i >= 0; i--) {            final int childIndex = getAndVerifyPreorderedIndex(                    childrenCount, i, customOrder);            final View child = getAndVerifyPreorderedView(                    preorderedList, children, childIndex);            // if there is a view that has accessibility focus we want it            // to get the event first and if not handled we will perform a            // normal dispatch. we may do a double iteration but this is            // safer given the timeframe.            if (childWithAccessibilityFocus != null) {                if (childWithAccessibilityFocus != child) {                    continue;                }                childWithAccessibilityFocus = null;                i = childrenCount - 1;            }//如果不能接收事件,直接continue            if (!canViewReceivePointerEvents(child)                    || !isTransformedTouchPointInView(x, y, child, null)) {                ev.setTargetAccessibilityFocus(false);                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);//分发事件到子组件            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {                // Child wants to receive touch within its bounds.                mLastTouchDownTime = ev.getDownTime();                if (preorderedList != null) {                    // childIndex points into presorted list, find original index                    for (int j = 0; j < childrenCount; j++) {                        if (children[childIndex] == mChildren[j]) {                            mLastTouchDownIndex = j;                            break;                        }                    }                } else {                    mLastTouchDownIndex = childIndex;                }                mLastTouchDownX = ev.getX();                mLastTouchDownY = ev.getY();                newTouchTarget = addTouchTarget(child, idBitsToAssign);                alreadyDispatchedToNewTouchTarget = true;                break;            }            // The accessibility focus didn't handle the event, so clear            // the flag and do a normal dispatch to all children.            ev.setTargetAccessibilityFocus(false);        }    }

查看dispatchTransformedTouchEvent方法

    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) {//调用了View 的 dispatchTouchEvent(event)                handled = super.dispatchTouchEvent(event);            } else {//子组件的事件分发                handled = child.dispatchTouchEvent(event);            }            event.setAction(oldAction);            return handled;        }//..省略}

这里为了方便理解,统一查看View的dispatchTouchEvent方法,其他组件可自行查看

    public boolean dispatchTouchEvent(MotionEvent event) {//..省略        boolean result = false;//..省略        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;            }        }//..省略        return result;    }

View 的 onTouchEvent方法

  public boolean onTouchEvent(MotionEvent event) {        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {            switch (action) {                case MotionEvent.ACTION_UP:                    //..省略                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {                        // This is a tap, so remove the longpress check                        removeLongPressCallback();                        // Only perform take click actions if we were in the pressed state                        if (!focusTaken) {                            // Use a Runnable and post this rather than calling                            // performClick directly. This lets other visual state                            // of the view update before click actions start.                            if (mPerformClick == null) {                                mPerformClick = new PerformClick();                            }                            if (!post(mPerformClick)) {                                //点击事件                                performClickInternal();                            }                        }                    }                    //..省略            }            return true;        }        //..省略    }
performClickInternal方法中调用了performClick方法:
    public boolean performClick() {        // We still need to call this method to handle the cases where performClick() was called        // externally, instead of through performClickInternal()        notifyAutofillManagerOnClick();        final boolean result;        final ListenerInfo li = mListenerInfo;        if (li != null && li.mOnClickListener != null) {            playSoundEffect(SoundEffectConstants.CLICK);//调用了OnClickListener.onClick(View view) 消费了本次事件            li.mOnClickListener.onClick(this);            result = true;        } else {            result = false;        }        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);        notifyEnterOrExitForAutoFillIfNeeded(true);        return result;    }

至此消费了事件,返回了true

更多相关文章

  1. Android(安卓)TextView跑马灯效果
  2. Android之选项菜单和上下文菜单解析
  3. Android中如何判断Intent是否存在
  4. android Fragment相关问题
  5. Android(安卓)模拟MotionEvent事件 触发输入法
  6. androidsetClickable不起作用没…
  7. Android利用soap WSDL与Webservice通信
  8. 【Android(安卓)Demo】图片之网格视图(GridView)
  9. Android之AsyncTask源码分析(第五篇:execute方法只能执行一次的原

随机推荐

  1. Android 服务(本地服务示例)(二)
  2. Android中使用GridView实现标签效果源码
  3. Android 联系人选择Widget
  4. Android中判断网络连接是否可用
  5. Failed to create directory C:\ Progra
  6. 百度地图中android获取经纬度和地方名称
  7. 【移动安全】Android App Smail代码动态
  8. Android Kill App
  9. android 服务service里面出Toast
  10. Android使用Retrofit请求WebService