Android事件分发机制浅谈(三)--源码分析(View篇)

     写事件分发源码分析的时候很纠结,网上的许多博文都是先分析的View,后分析ViewGroup。因为我一开始理解的时候是按我的流程图往下走的,感觉方向很对,单是具体分析的时候总是磕磕绊绊的,老要跳到View中去分析,很多方法理解不了,但毕竟流程清楚了许多,算是有得有失吧,不多说,开始分析。

     继续根据流程,先分析View的dispatchTouchEvent()方法,看源码。

     

 public boolean dispatchTouchEvent(MotionEvent event) {
        // If the event should be handled by accessibility focus first.
        if (event.isTargetAccessibilityFocus()) {
            // We don't have focus or no virtual descendant has it, do not handle the event.
            if (!isAccessibilityFocusedViewOrHost()) {
                return false;
            }
            // We have focus and got the event, then use normal event dispatch.
            event.setTargetAccessibilityFocus(false);
        }

        boolean result = false;

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // Defensive cleanup for new gesture
            stopNestedScroll();
        }

        if (onFilterTouchEventForSecurity(event)) {
            //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;
            }
        }

        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }

        // Clean up after nested scrolls if this is the end of a gesture;
        // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
        // of the gesture.
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

不得不说,比起ViewGroup这个方法真的很短。

        if (onFilterTouchEventForSecurity(event)) 

略过不必要的看这个判断中的方法判断当前View是否没被遮住等,接下来的就是我们曾在上一节中讲解过的

 1   ListenerInfo li = mListenerInfo;
 2             if (li != null && li.mOnTouchListener != null
 3                     && (mViewFlags & ENABLED_MASK) == ENABLED
 4                     && li.mOnTouchListener.onTouch(this, event)) {
 5                 result = true;
 6             }
 7 
 8             if (!result && onTouchEvent(event)) {
 9                 result = true;
10             }

这里不分析情景了,这里主要看下上一次没分析的onTouch方法和onTouchEvent方法

首先进入onTouch方法的源码

 boolean onTouch(View v, MotionEvent event);

才发现这只是个接口方法,看来是要重写的,那没什么好看的了,看onTouchEvent()方法

  1  public boolean onTouchEvent(MotionEvent event) {
  2         final float x = event.getX();
  3         final float y = event.getY();
  4         final int viewFlags = mViewFlags;
  5         final int action = event.getAction();
  6 
  7         if ((viewFlags & ENABLED_MASK) == DISABLED) {
  8             if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
  9                 setPressed(false);
 10             }
 11             // A disabled view that is clickable still consumes the touch
 12             // events, it just doesn't respond to them.
 13             return (((viewFlags & CLICKABLE) == CLICKABLE
 14                     || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
 15                     || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
 16         }
 17 
 18         if (mTouchDelegate != null) {
 19             if (mTouchDelegate.onTouchEvent(event)) {
 20                 return true;
 21             }
 22         }
 23 
 24         if (((viewFlags & CLICKABLE) == CLICKABLE ||
 25                 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
 26                 (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
 27             switch (action) {
 28                 case MotionEvent.ACTION_UP:
 29                     boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
 30                     if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
 31                         // take focus if we don't have it already and we should in
 32                         // touch mode.
 33                         boolean focusTaken = false;
 34                         if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
 35                             focusTaken = requestFocus();
 36                         }
 37 
 38                         if (prepressed) {
 39                             // The button is being released before we actually
 40                             // showed it as pressed.  Make it show the pressed
 41                             // state now (before scheduling the click) to ensure
 42                             // the user sees it.
 43                             setPressed(true, x, y);
 44                        }
 45 
 46                         if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
 47                             // This is a tap, so remove the longpress check
 48                             removeLongPressCallback();
 49 
 50                             // Only perform take click actions if we were in the pressed state
 51                             if (!focusTaken) {
 52                                 // Use a Runnable and post this rather than calling
 53                                 // performClick directly. This lets other visual state
 54                                 // of the view update before click actions start.
 55                                 if (mPerformClick == null) {
 56                                     mPerformClick = new PerformClick();
 57                                 }
 58                                 if (!post(mPerformClick)) {
 59                                     performClick();
 60                                 }
 61                             }
 62                         }
 63 
 64                         if (mUnsetPressedState == null) {
 65                             mUnsetPressedState = new UnsetPressedState();
 66                         }
 67 
 68                         if (prepressed) {
 69                             postDelayed(mUnsetPressedState,
 70                                     ViewConfiguration.getPressedStateDuration());
 71                         } else if (!post(mUnsetPressedState)) {
 72                             // If the post failed, unpress right now
 73                             mUnsetPressedState.run();
 74                         }
 75 
 76                         removeTapCallback();
 77                     }
 78                     mIgnoreNextUpEvent = false;
 79                     break;
 80 
 81                 case MotionEvent.ACTION_DOWN:
 82                     mHasPerformedLongPress = false;
 83 
 84                     if (performButtonActionOnTouchDown(event)) {
 85                         break;
 86                     }
 87 
 88                     // Walk up the hierarchy to determine if we're inside a scrolling container.
 89                     boolean isInScrollingContainer = isInScrollingContainer();
 90 
 91                     // For views inside a scrolling container, delay the pressed feedback for
 92                     // a short period in case this is a scroll.
 93                     if (isInScrollingContainer) {
 94                         mPrivateFlags |= PFLAG_PREPRESSED;
 95                         if (mPendingCheckForTap == null) {
 96                             mPendingCheckForTap = new CheckForTap();
 97                         }
 98                         mPendingCheckForTap.x = event.getX();
 99                         mPendingCheckForTap.y = event.getY();
100                         postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
101                     } else {
102                         // Not inside a scrolling container, so show the feedback right away
103                         setPressed(true, x, y);
104                         checkForLongClick(0);
105                     }
106                     break;
107 
108                 case MotionEvent.ACTION_CANCEL:
109                     setPressed(false);
110                     removeTapCallback();
111                     removeLongPressCallback();
112                     mInContextButtonPress = false;
113                     mHasPerformedLongPress = false;
114                     mIgnoreNextUpEvent = false;
115                     break;
116 
117                 case MotionEvent.ACTION_MOVE:
118                     drawableHotspotChanged(x, y);
119 
120                     // Be lenient about moving outside of buttons
121                     if (!pointInView(x, y, mTouchSlop)) {
122                         // Outside button
123                         removeTapCallback();
124                         if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
125                             // Remove any future long press/tap checks
126                             removeLongPressCallback();
127 
128                             setPressed(false);
129                         }
130                     }
131                     break;
132             }
133 
134             return true;
135         }
136 
137         return false;
138     }

突然发现好长,那么咱们分开看  

7-16 我们可以看到如果控件是disabled的同时是可以clickable的则onTouchEvent直接消费事件返回true,反之如果控件(View)是disenable状态,同时是disclickable的则onTouchEvent直接false。

24、137看到如果是disclickable的那么返回false

27-134是一个switch判断event的类型,对每个类型进行了适当的分析,有兴趣的可以自己了解一下

我们可以看到当ACTION_UP执行时,执行了一个perforClick方法

 1 public boolean performClick() {
 2         final boolean result;
 3         final ListenerInfo li = mListenerInfo;
 4         if (li != null && li.mOnClickListener != null) {
 5             playSoundEffect(SoundEffectConstants.CLICK);
 6             li.mOnClickListener.onClick(this);
 7             result = true;
 8         } else {
 9             result = false;
10         }
11 
12         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
13         return result;
14     }

这下我们可以看到if判断句中熟悉的方法了onCLick,原来onClick()方法是在这里调用的,同样的li.mOnClickListener是通过setOnClickListener方法设置的,现在是知道了为什么onClik方法的优先级较低了吧

 

好了,现在android的事件分析就告一段落了,写的时候因为很多,所以有些地方可能写的比较糊涂,有错误的话请指出。

 

posted @ 2017-01-01 16:03  YRLeaner  阅读(252)  评论(0编辑  收藏  举报