• Touch事件分发源码解析


    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    以下源码基于Gingerbread 2.3.7
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    1、先看ViewGroup的dispatchOnTouchEvent(MotionEvent e)的源码

    1.1 主要是获取一些坐标值,留备后用

     1     @Override
     2     public boolean dispatchTouchEvent(MotionEvent ev) {
     3         if (!onFilterTouchEventForSecurity(ev)) {
     4             return false;
     5         }
     6 
     7         final int action = ev.getAction();
     8         final float xf = ev.getX();
     9         final float yf = ev.getY();
    10         final float scrolledXFloat = xf + mScrollX;
    11         final float scrolledYFloat = yf + mScrollY;
    12         final Rect frame = mTempRect;
     

    1.2 先处理DOWN事件

     1         //TODO 1、判断是不是子View不允许老子拦截Touch事件
     2         boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
     3 
     4         if (action == MotionEvent.ACTION_DOWN) {
     5             if (mMotionTarget != null) {
     6                 // this is weird, we got a pen down, but we thought it was
     7                 // already down!
     8                 // XXX: We should probably send an ACTION_UP to the current
     9                 // target.
    10                 mMotionTarget = null;
    11             }
    12             // If we're disallowing intercept or if we're allowing and we didn't
    13             // intercept
    14             //TODO 2、如果子View不允许,或者老子自己不想拦截,则进入这里
    15             if (disallowIntercept || !onInterceptTouchEvent(ev)) {
    16                 // reset this event's action (just to protect ourselves)
    17                 ev.setAction(MotionEvent.ACTION_DOWN);
    18                 // We know we want to dispatch the event down, find a child
    19                 // who can handle it, start with the front-most child.
    20                 final int scrolledXInt = (int) scrolledXFloat;
    21                 final int scrolledYInt = (int) scrolledYFloat;
    22                 final View[] children = mChildren;
    23                 final int count = mChildrenCount;
    24                 //TODO 3、根据用户落指的坐标,找到应该响应该Touch事件的子View或者子ViewGroup
    25                 for (int i = count - 1; i >= 0; i--) {
    26                     final View child = children[i];
    27                     if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
    28                             || child.getAnimation() != null) {
    29                         child.getHitRect(frame);
    30                         //TODO 4、很明显,根据范围来判断的
    31                         if (frame.contains(scrolledXInt, scrolledYInt)) {
    32                             // offset the event to the view's coordinate system
    33                             final float xc = scrolledXFloat - child.mLeft;
    34                             final float yc = scrolledYFloat - child.mTop;
    35                             ev.setLocation(xc, yc);
    36                             child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
    37                             //TODO 5、找到子View,将事件交给它,如果它愿意消费DOWN事件,则记录下来这个龟儿子,并返回
    39                             if (child.dispatchTouchEvent(ev))  {
    40                                 // Event handled, we have a target now.
    41                                 mMotionTarget = child;
    42                                 return true;
    43                             }
    44                             // The event didn't get handled, try the next view.
    45                             // Don't reset the event's location, it's not
    46                             // necessary here.
    47                         }
    48                     }
    49                 }
    50             }
    51         }

    看见上面5条注释了吧,够用了

    1.3 没找到子View、子View不愿消费(注释5)、或者不是DOWN事件,如下

     1         boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
     2                 (action == MotionEvent.ACTION_CANCEL);
     3 
     4         if (isUpOrCancel) {
     5             // Note, we've already copied the previous state to our local
     6             // variable, so this takes effect on the next event
     7             mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
     8         }
     9 
    10         // The event wasn't an ACTION_DOWN, dispatch it to our target if
    11         // we have one.
    12         // TODO 1、如果是没有子View,不管是DOWN还是其他事件,都交给自己来处理,包括后续的MOVE、UP事件。
    13         //      因为这个方法有点类似递归调用,所以如果自己也不想处理,那么父类会分发给自己
    14         final View target = mMotionTarget;
    15         if (target == null) {
    16             // We don't have a target, this means we're handling the
    17             // event as a regular view.
    18             ev.setLocation(xf, yf);
    19             if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
    20                 ev.setAction(MotionEvent.ACTION_CANCEL);
    21                 mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
    22             }
    23             // TODO 2、调用View的dispatchTouchEvent,会调用到自己的onTouchEvent()方法
    24             return super.dispatchTouchEvent(ev);
    25         }

    这段代码就一个意思,不管啥事件,没有子View,老子亲自处理~

    1.4 找到子View,继续往下走

     1         // TODO 1、找到子View,子View让老子拦截,或者老子自己想拦截,给龟孙一个CANCEL,
     2         //      并告诉父ViewGroup,老子要了,而且将mMotionTarget置为null,这样后续事件直接走上面的判断,直接找老子处理
     3         // if have a target, see if we're allowed to and want to intercept its
     4         // events
     5         if (!disallowIntercept && onInterceptTouchEvent(ev)) {
     6             final float xc = scrolledXFloat - (float) target.mLeft;
     7             final float yc = scrolledYFloat - (float) target.mTop;
     8             mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
     9             ev.setAction(MotionEvent.ACTION_CANCEL);
    10             ev.setLocation(xc, yc);
    11             if (!target.dispatchTouchEvent(ev)) {
    12                 // target didn't handle ACTION_CANCEL. not much we can do
    13                 // but they should have.
    14             }
    15             // clear the target
    16             mMotionTarget = null;
    17             // Don't dispatch this event to our own view, because we already
    18             // saw it when intercepting; we just want to give the following
    19             // event to the normal onTouchEvent().
    20             return true;
    21         }
    22 
    23         // TODO 2、如果是UP、CANCEL事件,则清空
    24         if (isUpOrCancel) {
    25             mMotionTarget = null;
    26         }

    这段代码,龟孙之前说的好好的, 要自己消费事件,突然又不想了。或者老子突然想拦截了,那么老子自己拦截,自己处理

    1.5 没有幺蛾子了,交给龟孙自己处理

     1         // finally offset the event to the target's coordinate system and
     2         // dispatch the event.
     3         final float xc = scrolledXFloat - (float) target.mLeft;
     4         final float yc = scrolledYFloat - (float) target.mTop;
     5         ev.setLocation(xc, yc);
     6 
     7         if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
     8             ev.setAction(MotionEvent.ACTION_CANCEL);
     9             target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
    10             mMotionTarget = null;
    11         }
    12 
    13         // TODO 1、不管前面这几行了,交给龟孙去处理
    14         return target.dispatchTouchEvent(ev);

    ViewGroup没有重写View的onTouchEvent()方法,onInterceptTouchEvent()返回false,默认不拦截

    2、 View的相关方法

    2.1 dispatchTouchEvent()方法

    就一点点,调用自己的onTouchEvent()方法,并以onTouchEvent的返回值为返回值

     1     /**
     2      * Pass the touch screen motion event down to the target view, or this
     3      * view if it is the target.
     4      *
     5      * @param event The motion event to be dispatched.
     6      * @return True if the event was handled by the view, false otherwise.
     7      */
     8     public boolean dispatchTouchEvent(MotionEvent event) {
     9         if (!onFilterTouchEventForSecurity(event)) {
    10             return false;
    11         }
    12 
    13         if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
    14                 mOnTouchListener.onTouch(this, event)) {
    15             return true;
    16         }
    17         return onTouchEvent(event);
    18     }

    2.2 onTouchEvent()方法

    这个方法比较复杂,不可点击的时候返回false,说明自己不处理。可点击的时候,根据不同的事件类型进行处理

      1     public boolean onTouchEvent(MotionEvent event) {
      2         final int viewFlags = mViewFlags;
      3 
      4         if ((viewFlags & ENABLED_MASK) == DISABLED) {
      5             // A disabled view that is clickable still consumes the touch
      6             // events, it just doesn't respond to them.
      7             return (((viewFlags & CLICKABLE) == CLICKABLE ||
      8                     (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
      9         }
     10 
     11         if (mTouchDelegate != null) {
     12             if (mTouchDelegate.onTouchEvent(event)) {
     13                 return true;
     14             }
     15         }
     16 
     17         if (((viewFlags & CLICKABLE) == CLICKABLE ||
     18                 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
     19             switch (event.getAction()) {
     20                 case MotionEvent.ACTION_UP:
     21                     boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
     22                     if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
     23                         // take focus if we don't have it already and we should in
     24                         // touch mode.
     25                         boolean focusTaken = false;
     26                         if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
     27                             focusTaken = requestFocus();
     28                         }
     29 
     30                         if (!mHasPerformedLongPress) {
     31                             // This is a tap, so remove the longpress check
     32                             removeLongPressCallback();
     33 
     34                             // Only perform take click actions if we were in the pressed state
     35                             if (!focusTaken) {
     36                                 // Use a Runnable and post this rather than calling
     37                                 // performClick directly. This lets other visual state
     38                                 // of the view update before click actions start.
     39                                 if (mPerformClick == null) {
     40                                     mPerformClick = new PerformClick();
     41                                 }
     42                                 if (!post(mPerformClick)) {
     43                                     performClick();
     44                                 }
     45                             }
     46                         }
     47 
     48                         if (mUnsetPressedState == null) {
     49                             mUnsetPressedState = new UnsetPressedState();
     50                         }
     51 
     52                         if (prepressed) {
     53                             mPrivateFlags |= PRESSED;
     54                             refreshDrawableState();
     55                             postDelayed(mUnsetPressedState,
     56                                     ViewConfiguration.getPressedStateDuration());
     57                         } else if (!post(mUnsetPressedState)) {
     58                             // If the post failed, unpress right now
     59                             mUnsetPressedState.run();
     60                         }
     61                         removeTapCallback();
     62                     }
     63                     break;
     64 
     65                 case MotionEvent.ACTION_DOWN:
     66                     if (mPendingCheckForTap == null) {
     67                         mPendingCheckForTap = new CheckForTap();
     68                     }
     69                     mPrivateFlags |= PREPRESSED;
     70                     mHasPerformedLongPress = false;
     71                     postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
     72                     break;
     73 
     74                 case MotionEvent.ACTION_CANCEL:
     75                     mPrivateFlags &= ~PRESSED;
     76                     refreshDrawableState();
     77                     removeTapCallback();
     78                     break;
     79 
     80                 case MotionEvent.ACTION_MOVE:
     81                     final int x = (int) event.getX();
     82                     final int y = (int) event.getY();
     83 
     84                     // Be lenient about moving outside of buttons
     85                     int slop = mTouchSlop;
     86                     if ((x < 0 - slop) || (x >= getWidth() + slop) ||
     87                             (y < 0 - slop) || (y >= getHeight() + slop)) {
     88                         // Outside button
     89                         removeTapCallback();
     90                         if ((mPrivateFlags & PRESSED) != 0) {
     91                             // Remove any future long press/tap checks
     92                             removeLongPressCallback();
     93 
     94                             // Need to switch from pressed to not pressed
     95                             mPrivateFlags &= ~PRESSED;
     96                             refreshDrawableState();
     97                         }
     98                     }
     99                     break;
    100             }
    101             return true;
    102         }
    103 
    104         return false;
    105     }
     
      
  • 相关阅读:
    Docker界面化管理
    搭建MQTT服务器(Docker版)
    VS Code Markdown文件实时预览
    Nginx直接处理接口请求,返回相应内容(带html标签)
    Docker(九): 安装MySQL主从复制
    feign的一个注解居然隐藏这么多知识!
    使用Magisk指令修改 ro.debuggable(不刷机)
    【钓鱼可用】文件名反转字符串
    android高级UI之贝塞尔曲线<下>--贝塞尔曲线运用:QQ消息气泡
    英文阅读技巧操练---Article 1:The Product-Minded Software Engineer《一》
  • 原文地址:https://www.cnblogs.com/wlrhnh/p/10252080.html
Copyright © 2020-2023  润新知