【Android】事件分发和处理机制

【Android】事件分发和处理机制

我们首先要知道的是,Android中的事件是用MotionEvent对象来表示,那么它的哪些类型呢?

事件类型触发时机
ACTION_DOWN手指初接触到屏幕时触发
ACTION_MOVE手指在屏幕上滑动时触发,会多次触发
ACTION_UP手指离开屏幕时触发
ACTION_CANCEL事件被上层拦截时触发

事件分发,意思就是把事件从一个地方通过某种逻辑分发给其他控件去处理,一般来说,事件都会经过Activity,然后由Activity往下传递到ViewGroup,然后ViewGroup再分发给它的子view

Activity —> ViewGroup —> 子view

事件给到子ViewGroup后,如果它的子view不需要这个事件,或者它都没有子view,那么这个事件就只能由ViewGroup自己来处理,否则事件就由子view来处理。

那么,事件一定会经过Activity吗?

其实不是的,我们的程序界面的顶层ViewGroup,也就是DecorView中注册了 Activity 这个callBack,所以程序的主界面接收到事件之后会先交给 Activity

但是,如果是另外的控件树,如dialog、popupWindow等事件流是不会经过Activity的。只有自己界面的事件才会经Activity。

对于View而言,我们只需要关注事件的处理即可,因为事件传到View,已经相当于是最下层了,不能再继续分发了。

View对事件的处理,是在View.dispatchTouchEvent方法中处理的。

下面我们就来看看View的事件处理机制,然后再看ViewGroup的事件分发机制。

事件处理

我们在对一个view进行点击事件监听和触摸监听的时候,一般都是这样的写的:

//监听view的点击事件
btn.setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View?) {
        Log.i("testLog", "onClick.")
    }
})

//监听view的触摸事件
btn.setOnTouchListener(object : View.OnTouchListener {
            override fun onTouch(v: View?, m: MotionEvent?): Boolean {
                Log.i("testLog", "onTouch. MotionEvent=${m?.action}")
                return false
            }
})

用法非常简单,相信大家都已经熟的不能再熟了。但是,我们的重点不在于用法,而是在于原理上,那么问题来了:

问题一:onTouch方法,它的返回值是Boolean类型的,返回 true 和 false 分别有什么用呢?

问题二:onTouchonClick这两个方法,谁先调用?在哪调用呢?

带着这以上问题,我们直接继续往下来看。

假设我们在当前view的onTouch方法返回false,如下:

val btn = findViewById<Button>(R.id.btn)

btn.setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View?) {
        Log.i("testLog", "onClick.")
    }
})

btn.setOnTouchListener(object : View.OnTouchListener {
    override fun onTouch(v: View?, m: MotionEvent?): Boolean {
        Log.i("testLog", "onTouch. MotionEvent=${m?.action}")
        return false
    }
})

然后我们点击view,查看输出的日志:

在这里插入图片描述

可以看到,当我们把view的onTouch方法返回false,那么onTouch和onClick都能正常回调了,并且onTouch方法比onClick方法先回调

然后我们再将view的onTouch方法返回true,然后点击view,日志输出如下:

在这里插入图片描述

可以看到,只有onTouch方法中打印的日志能输出,onClick方法中的日志并没有输出,也就是说,

如果view在它的onTouch方法中返回true,会导致它的onClick方法无法被回调

那么,为什么view的onTouch方法返回true,会导致它的onClick方法无法回调了呢?

下面我们就开始进入源码环节:

上面我们说过,View的事件处理,是在View.dispatchTouchEvent方法中处理的,所以我们进入View.java类,看看这个方法的实现:

View:
public boolean dispatchTouchEvent(MotionEvent event) {
    // …………………………………………………………………………………………………………………………………………………………………………
    boolean result = false;
    // …………………………………………………………………………………………………………………………………………………………………………
    // 判断事件是否安全,一般正常的事件都是能进入这个if内的
    if (onFilterTouchEventForSecurity(event)) {
        if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
            result = true;
        }
        ListenerInfo li = mListenerInfo;
        /**
        * 1、li:只要我们调用了setOnTouchListener,li就不会为null。
        * 2、li.mOnTouchListener: 是指setOnTouchListener方法里的OnTouchListener参数,只要正常传入,也不会为null
        * 3、view的使能,如果view是可以点击的(enable),那么(mViewFlags & ENABLED_MASK) == ENABLED 就会为 ture
        * 4、onTouch方法的执行,并判断它的返回值
        * 5、这块if语句,主要取决于onTouch方法的返回值,如果onTouch方法返回true,那么就会将true赋值给result变量
        **/
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
        }

        /**
        * 1、result的值如果为false,才能继续执行onTouchEvent(event)方法
        * 2、执行onTouchEvent(event)方法,并判断它的返回值。
        * 3、如果result的值为false,且onTouchEvent(event)返回true,那么也会将result赋值为true
        **/ 
        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }
    // …………………………………………………………………………………………………………………………………………………………………………
    return result;
}

View.dispatchTouchEvent方法中,我只留下了核心部分的代码,我们来分析一下这部分代码,注释里也写得比较明白了:

首先,在我们调用了view的setOnTouchListener(OnTouchListener list)方法,并且该View是可点击的前提下

如果OnTouchListener.onTouch方法的返回值为true,那么result变量就会被赋值为true

此时,如果result被赋值为true了,就会导致onTouchEvent(event)不会被执行

反之,如果OnTouchListener.onTouch方法的返回值为false,那么onTouchEvent(event)才会被执行,

然后,再根据onTouchEvent(event)的返回值,决定要不要将result赋值为true。

需要注意的是:

如果onTouchEvent(event)方法返回true,那么表示该方法消费了此次事件,如果返回 false,那么表示该方法并未处理完全,该事件仍然需要以某种方式传递下去继续等待处理。

这时候我们可以大胆猜测一下,刚刚在我们的演示demo上,在onTouch方法返回值设置为true,就会导致onClick方法无法执行。

所以有没有可能onClick的执行是在onTouchEvent(event)方法中执行的呢?

带着这个猜测,我们进入onTouchEvent方法看看:

public boolean onTouchEvent(MotionEvent event) {
    // …………………………………………………………………………………………………………………………………………………………………………
    if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
        switch (action) {
            case MotionEvent.ACTION_UP:
            // …………………………………………………………………………………………………………………………………………………………………………
                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                        removeLongPressCallback();
       
                        if (!focusTaken) {
    
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                            if (!post(mPerformClick)) {
                                performClickInternal();
                            }
                        }
                    }
                    removeTapCallback();
                }
                mIgnoreNextUpEvent = false;
                break;

            case MotionEvent.ACTION_DOWN:
                // …………………………………………………………………………………………………………………………………………………………………………
                break;

            case MotionEvent.ACTION_CANCEL:
                // …………………………………………………………………………………………………………………………………………………………………………
                break;

            case MotionEvent.ACTION_MOVE:
                // …………………………………………………………………………………………………………………………………………………………………………
                break;
        }

        return true;
    }

    return false;
}

这个方法还挺长的,我们从刚刚的演示代码中也看到了,onClick是在ACTION_UP事件后才回调的,所以我们直接看到里面处理ACTION_UP的代码吧,可以看到,

这里面构建了一个PerformClick对象,它其实是个Runnable对象,然后通过post方法执行这个Runnable,我们进入PerformClick这个类看看:

View.java:
private final class PerformClick implements Runnable {
    @Override
    public void run() {
        recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);
        // 执行了performClickInternal方法。
        performClickInternal();
    }
}

private boolean performClickInternal() {
        notifyAutofillManagerOnClick();
        return performClick();
}

// performClick最终会走到performClick()方法
public boolean performClick() {
        notifyAutofillManagerOnClick();
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        
        /**
        * 1、li:只要我们调用了setOnClickListener, li就不会为null。
        * 2、li.mOnClickListener:指的是setOnClickListener方法里的OnClickListener参数,只要正常传入,也不会为null
        **/
        if (li != null && li.mOnClickListener != null) {
        	//点击音效处理
            playSoundEffect(SoundEffectConstants.CLICK);
            //执行onClick方法
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

        notifyEnterOrExitForAutoFillIfNeeded(true);

        return result;
}

源码中可以看到,performClick会走到performClickInternal方法,然后最终会走到performClick()方法performClick()方法中,只要我们调用了setOnClickListener()方法并传入了OnClickListener参数,那么OnClickListener.onClick()就会被执行。

事件处理的总结

最后,我来梳理一下这整个事件从onTouch到onClick的处理流程,

前提:View拿到事件,并执行dispatchTouchEvent(event)方法。

1、如果设置了View的setOnTouchListener(OnTouchListener list),并且该view是可点击的;

2、执行onTouch(this, event)方法,如果onTouch方法的返回值是false

3、执行onTouchEvent(event)方法;

4、在onTouchEvent(event)方法中处理ACTION_UP事件,最终执行了performClick()方法;

5、在performClick()方法判断,如果设置了View的setOnClickListener(OnClickListener listen)方法,则执行onClick(this)方法。

事件分发

事件产生后,事件是Activity先收到的,所以会先执行ActivitydispatchTouchEvent()方法:

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

在这个方法中,我们先看到第二个if语句,调用看getWindow().superDispatchTouchEvent()方法,继续将事件交给PhoneWindow来处理,

如果getWindow().superDispatchTouchEvent()返回值为ture,那么整个方法就返回true了,Activity后续就不会再对这个事件进行处理。

如果getWindow().superDispatchTouchEvent()返回值为false,那么就会执行onTouchEvent(ev)方法,事件将由Activity进行处理。

PhoneWindow.java:
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}

DecorView.java:
public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
}

然后,PhoneWindow又会将事件交给DecorView来处理。DecorView又会把事件交给它的父类来处理,

DecorView继承自FrameLayout,但是我们走进FrameLayout源码里面看,其实FrameLayout是没有重写dispatchTouchEvent()方法的,所以,,,

执行DecorView的dispatchTouchEvent()方法,实际上就是执行了ViewGroup类的dispatchTouchEvent()方法。

在View中的dispatchTouchEvent方法,它的作用是用来进行事件处理的,

而在ViewGroup中,重写了父类(View)的dispatchTouchEvent方法,并将dispatchTouchEvent方法改成了事件的分发。

在ViewGroup的dispatchTouchEvent(MotionEvent ev) 方法中,就会进行事件的分发了,我们来看看方法的源码:

ViewGroup.java:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    // ..........................................................................................

    /**
     * 第一部分核心代码
     **/
    final boolean intercepted;
    if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
        //如果当前事件是ACTION_DOWN,或者mFirstTouchTarget变量不为null,才能走进来
        final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
        if (!disallowIntercept) {
            //如果disallowIntercept变量为false,就执行onInterceptTouchEvent方法,并将其返回值并赋值给intercepted变量
            intercepted = onInterceptTouchEvent(ev);
            ev.setAction(action);
        } else {
            //如果disallowIntercept变量为true,intercepted变量也会为false
            intercepted = false;
        }
    } else {
        如果当前事件不是DOWN,并且,mFirstTouchTarget变量为null,则intercepted变量等于true
        intercepted = true;
    }

    // ..........................................................................................

    /**
     * 第二部分核心代码
     **/
    if (!canceled && !intercepted) {
        ............................................................
        //如果事件没有被canceled 且 【intercepted值为false】,就把事件分发给子view
        //注意:只有事件为ACTION_DOWN的时候,这个if块才有机会执行
        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 = isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);
                final float y = isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);

                //在这里对子view进行排序,确定事件先给哪个子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--) {
                    // ..........................................................................................
                    //通过dispatchTransformedTouchEvent这个方法,把child方法传进去,对child进行事件的处理
                    if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                            ............................................................
                        //在这里会对mFirstTouchTarget这个变量进行赋值,
                        newTouchTarget = addTouchTarget(child, idBitsToAssign);
                        alreadyDispatchedToNewTouchTarget = true;
                        //如果能走进这个if块,那就说明子view把事件处理了,那么直接把整个for循环终止,其他子view就不会再收到这个事件
                        break;
                    }

                    // The accessibility focus didn't handle the event, so clear
                    // the flag and do a normal dispatch to all children.
                    ev.setTargetAccessibilityFocus(false);
                }
                if (preorderedList != null) preorderedList.clear();
            }
        }
        // ..........................................................................................
    }


    /**
     * 第三部分核心代码
     **/
    if (mFirstTouchTarget == null) {
        //如果mFirstTouchTarget为null,事件由当前view处理
        handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS);
    } else {
        //如果mFirstTouchTarget不为null,事件由其子view处理
        TouchTarget predecessor = null;
        TouchTarget target = mFirstTouchTarget;
        //这个循环实际上只会执行一次【多指操作】
        while (target != null) {
            final TouchTarget next = target.next;
            if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                handled = true;
            } else {
                final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
                //处理子view事件
                if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {
                    handled = true;
                }
                if (cancelChild) {
                    if (predecessor == null) {
                        mFirstTouchTarget = next;
                    } else {
                        predecessor.next = next;
                    }
                    target.recycle();
                    target = next;
                    continue;
                }
            }
            predecessor = target;
            target = next;
        }
    }
// ..........................................................................................
    return handled;
}

ViewGroup的dispatchTouchEvent(MotionEvent ev)这个方法非常长,我们只看主要核心的流程,我们将它分为以下三个核心部分:

一、第一部分:

/**
 * 第一部分核心代码
 **/
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
    //如果当前事件是ACTION_DOWN,或者mFirstTouchTarget变量不为null,才能走进来
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    if (!disallowIntercept) {
        //如果disallowIntercept变量为false,就执行onInterceptTouchEvent方法,并将其返回值并赋值给intercepted变量
        intercepted = onInterceptTouchEvent(ev);
        ev.setAction(action);
    } else {
        //如果disallowIntercept变量为true,intercepted变量也会为false
        intercepted = false;
    }
} else {
    //如果当前事件不是DOWN,并且,mFirstTouchTarget变量为null,则intercepted变量等于true
    intercepted = true;
}

流程如下:

1、如果当前事件是DOWN,或者mFirstTouchTarget变量不为null,则走 if 里的代码;否则就直接将intercepted变量赋值为true,

intercepted变量,代表事件有没有被当前View的ViewGroup拦截,为true表示已被它的ViewGroup拦截

mFirstTouchTarget变量:当事件被子view处理了,那么mFirstTouchTarget就会被赋值,如果事件没有被任何子view处理,那么mFirstTouchTarget的值就默认为null

2、能走到 if 里的代码,说明事件没有被它的ViewGroup拦截。如果disallowIntercept变量为false,就执行onInterceptTouchEvent(ev)方法,并将其返回值并赋值给intercepted变量;

3、如果disallowIntercept变量为trueintercepted变量直接赋值为false。

onInterceptTouchEvent(ev)方法是干嘛的呢?

它是用来拦截事件的,如果返回值为true,那么就会把事件拦截下来,然后事件就不能继续分发了,所以onInterceptTouchEvent(ev)的返回值决定了事件是否继续分发

上面的变量intercepted,会第二部分的代码中会被用到。

二、第二部分:

```java
/**
* 第二部分核心代码
**/
if (!canceled && !intercepted) {
      // ..........................................................................................
      // 如果事件没有被canceled 且 【intercepted值为false】,就把事件分发给子view
      // 注意:只有事件为ACTION_DOWN的时候,这个 if 块才有机会执行
      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 = isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);
              final float y = isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);
          // 在这里对子view进行排序,确定事件先给哪个子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--) {
                  ............................................................
              // 通过dispatchTransformedTouchEvent这个方法,把child传进去,对child进行事件的处理
              if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                  // ..........................................................................................
                  // 在这里会对mFirstTouchTarget这个变量进行赋值,
                  newTouchTarget = addTouchTarget(child, idBitsToAssign);
                  alreadyDispatchedToNewTouchTarget = true;
                  //如果能走进这个if块,那就说明子view把事件处理了,那么直接把整个for循环终止,其他子view就不会再收到这个事件
                  break;
              }

              // The accessibility focus didn't handle the event, so clear
              // the flag and do a normal dispatch to all children.
              ev.setTargetAccessibilityFocus(false);
          }
          if (preorderedList != null) preorderedList.clear();
      }
  }
  // ..........................................................................................
}
```

流程如下:

1、事件是DOWN事件的前提下,如果事件没有被canceled 没有被ViewGroup拦截,即intercepted值为false,就把事件分发给子view

2、对子view进行排序,确定事件先给哪个子view处理

3、循环遍历子view,每遍历到一个子view就执行一次dispatchTransformedTouchEvent方法,并把child(即子View)传进去,对child进行事件的处理

4、dispatchTransformedTouchEvent如果返回true,说明这个子view把事件处理了,则mFirstTouchTarget将会被赋值,其他子view将不会收到事件,循环结束。

三、第三部分:

/**
 * 第三部分核心代码
 **/
 if (mFirstTouchTarget == null) {
        //如果mFirstTouchTarget为null,事件由当前view处理
        handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS);
    } else {
        //如果mFirstTouchTarget不为null,事件由其子view处理
        TouchTarget predecessor = null;
        TouchTarget target = mFirstTouchTarget;
        //这个循环实际上只会执行一次【多指操作】
        while (target != null) {
            final TouchTarget next = target.next;
            if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                handled = true;
            } else {
                final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
                //处理子view事件
                if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {
                    handled = true;
                }
                if (cancelChild) {
                    if (predecessor == null) {
                        mFirstTouchTarget = next;
                    } else {
                        predecessor.next = next;
                    }
                    target.recycle();
                    target = next;
                    continue;
                }
            }
            predecessor = target;
            target = next;
        }
 }

流程如下:

1、如果mFirstTouchTargetnull,说明该ViewGroup的子View并没有处理掉事件,所以该事件将由当前view自己处理

2、如果mFirstTouchTarget不为null,事件由其子view处理

【mFirstTouchTarget】这个变量,是在第二部分那里赋值的,当事件被子view处理了,那么mFirstTouchTarget就会被赋值,如果事件没有被任何子view处理,那么mFirstTouchTarget的值就默认为null。另外,不管是事件由当前view处理,还是由子view处理,他们都执行了dispatchTransformedTouchEvent方法,只是入参不同而已。

那么,我们继续进入dispatchTransformedTouchEvent方法看看:

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
                                              View child, int desiredPointerIdBits) {
    ............................................................
    if (child == null) {
        //如果child == null,说明是当前的view,直接对当前view进行事件的处理
        handled = super.dispatchTouchEvent(transformedEvent);
    } else {
        //如果child不为null,说明是当前是子view,开始对子view进行事件的处理
        final float offsetX = mScrollX - child.mLeft;
        final float offsetY = mScrollY - child.mTop;
        transformedEvent.offsetLocation(offsetX, offsetY);
        if (!child.hasIdentityMatrix()) {
            transformedEvent.transform(child.getInverseMatrix());
        }

        /**
         * 在这里真正对子view进行事件处理,执行child.dispatchTouchEvent(transformedEvent)方法
         * 如果子view把事件处理了,那么这里就会返回true
         **/
        handled = child.dispatchTouchEvent(transformedEvent);
    }
    transformedEvent.recycle();
    return handled;
}

这个方法对child参数进行判断,如果child == null,说明是当前的 view ,直接对当前view进行事件的处理,

即调用**super.dispatchTouchEvent(transformedEvent)**方法,并把返回值赋值给handled变量;

如果child不为null,说明是当前是子view,就对子view进行事件的处理,

即调用child.dispatchTouchEvent(transformedEvent)方法,并把返回值赋值给handled变量;

正常情况下,如果view把事件成功处理了,handled变量就会为true

dispatchTransformedTouchEvent这个方法处理完,就会把handled返回到上一个方法,即dispatchTouchEvent(MotionEvent ev)方法,如果dispatchTouchEvent(MotionEvent ev)方法返回了true,说明事件已经被处理了。

事件分发的总结

那么最后,我们对事件的分发流程做个总结:

1、事件首先传递给Activity

2、Activity将事件传给PhoneWindow处理(如果PhoneWindow不处理,那就自己处理)

3、Activity将事件传给PhoneWindow后,PhoneWindow继续传给DecorView处理

4、DecorView调用ViewGroup的dispatchTouchEvent()对事件进行分发

5、调用onInterceptTouchEvent方法判断是否要进行拦截

6、对intercepted变量进行赋值。如果事件不是DOWN 且 mFirstTouchTarget变量为null,则intercepted变量赋值为true;否则如果disallowIntercept变量为false,就执行onInterceptTouchEvent方法(判断是否要拦截事件),并将其返回值赋值给intercepted变量;如果disallowIntercept变量为true,intercepted变量直接赋值为false。

7、事件是DOWN事件的前提下,如果事件没有被canceled 且 事件没有被ViewGroup拦截 (intercepted值为false),就把事件分发给子view

8、对子view进行排序,确定事件先给哪个子view处理

9、循环遍历子view,每遍历到一个子view就执行一次dispatchTransformedTouchEvent方法,把child传进去,对child进行事件的处理

10、dispatchTransformedTouchEvent如果返回true,说明这个子view把事件处理了,则mFirstTouchTarget将会被赋值,其他子view将不会收到事件,循环结束。

11、如果mFirstTouchTarget为null,事件由当前view自己处理

12、如果mFirstTouchTarget不为null,事件由其子view处理

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一场雪ycx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值