Touch event of View source code analysis

The timing of calling several methods of several touch events of View is not very clear, and I have not remembered it several times. Today, let's take a look at how the source code is implemented.

View event dispatch:

In fact, View has no event distribution, only one event is processed or not processed, but the methods of event distribution we are talking about are indeed there. We look at the dispatchTouchEvent method of View:

View.dispatchTouchEvent(motionEvent event):

 public boolean dispatchTouchEvent(MotionEvent event) {

      ...

        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
          /**

          我们可以清楚的看到如果我们设置了OnTouchListener,那么下面的方法肯定会调用
          但是:OnTouchListener的ontouch方法是有返回值的,(默认返回false)

          返回:true  ->  result = true;那么onTouchEvent(event)就不会被调用了.
                false ->  result = false,那么就会调用onTouchEvent(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;
            }
        }

    ...

        return result;
    }

Next, let's take a look at the View's onTouchEvent(event) method:

View.onTouchEvent(event):

  public boolean onTouchEvent(MotionEvent event) {
      ...
       if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {

                 case MotionEvent.ACTION_UP:

                  if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {

                       ...

                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                            removeLongPressCallback();

                            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)) {
                                /**
                                这个方法就是处理点击事件的回调接口

                                这个方法是在我们手指抬起的时候调用的

                                */

                                    performClick();
                                }
                            }
                        }

                        ...

                     }   
                    break;

                 case MotionEvent.ACTION_DOWN:
                    break;
                  case MotionEvent.ACTION_CANCEL:
                    break;
                 case MotionEvent.ACTION_MOVE:
                    break;

            }

              return true;
        }


        return false;
  }

The logic is very simple. Only when our finger is pressed or moved, it does not return false, then the event of the finger is lifted. It is possible to respond to the click event. If false is returned in the down event, it means that the view does not handle this event. Events will not be handed over to it in the future.

Event dispatch of ViewGroup:

The event handling mechanism of View is mentioned above, then let's see how ViewGroup handles event distribution:

ViewGroup.dispatchTouchEvent(MotionEvent ev):

 @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {//这个事件分发是从父容器中调用过来的

         if (onFilterTouchEventForSecurity(ev)) {

           ...

            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    /**
                    当手指down或者是mFirstTouchTarget不为空的时候,
                    也就是说一次新的按下事件或者是之前处理过的事件有需要下发的时候
                    调用onInterceptTouchEvent方法.这个方法中也就是做了一些判断,

                    默认是返回fasle的

                    如果我们重写了这个方法那就不一定了.

                    */

                    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;
            }

           ...

            if (!canceled && !intercepted) {

              //刚才默认onInterceptTouchEvent返回false,那么就会进入这个方法

                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

                    if (newTouchTarget == null && childrenCount != 0) {
                       //循坏遍历同一个父容器中相同位置的子控件,为了方便理解,
                       //我们可以想象为relativeLayout控件叠加时候的情况.

                        for (int i = childrenCount - 1; i >= 0; i--) {

                           /**

                           这个事件分发的一个重要的方法

                           主要是用来处理传入的孩子是不是需要继续分发,


                          如果孩子为空,那么就会将事件返回给父容器或者活动来处理,
                          要是可以处理那么就来处理


                           */
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                               //如果处理成功,意思就是有下发事件有子View处理了
                               //那么就将这个子View添加到链表中,用来下次事件的处理.

                                newTouchTarget = addTouchTarget(child, idBitsToAssign);

                                break;
                            }


                        }

                    }

                    if (newTouchTarget == null && mFirstTouchTarget != null) {

                        newTouchTarget = mFirstTouchTarget;
                        while (newTouchTarget.next != null) {
                            newTouchTarget = newTouchTarget.next;
                        }
                        newTouchTarget.pointerIdBits |= idBitsToAssign;
                    }
                }
            }

            // Dispatch to touch targets.
            if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {

               //循环在链表中取得之前处理过的事件target,用于处理
            }


        }

          return handled;
    }

By checking the source code of this event distribution, we can now know that Qin Chu:

Summarize:

  • When the onInterceptTouchEvent method of the ViewGroup returns true, the onTouchEvent method is called. (Of course, this method does not exist in View)
  • When we set the OnTouchListenter method to a View, then if this method will be called first, but if true is returned, then there will be no subsequent methods. If it returns false, the OnTouchEvent method will be called, and finally the onClick method will be called.
  • If the onTouchEvent event of a View returns false, it means that the View does not handle this event, and the OnTouchEvnet method of the parent container will be called. This is a typical Chain of Responsibility pattern
  • An event sequence is that when the finger is down->move->move....->up. The same sequence of events can only be consumed by the same View.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324830145&siteId=291194637