• 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的事件分析就告一段落了,写的时候因为很多,所以有些地方可能写的比较糊涂,有错误的话请指出。

  • 相关阅读:
    Vue 常用指令
    Vue起飞前的准备
    Django ORM 高性能查询优化
    Django 缓存、序列化、信号
    关于Django ModelForm渲染时间格式问题
    Django自定义分页并保存搜索条件
    Docker容器中用户权限管理
    Linux三种SSH协议登陆方式
    Docker部署Nextcloud私有网盘
    Zabbix官方部署搭建
  • 原文地址:https://www.cnblogs.com/yrstudy/p/6241089.html
Copyright © 2020-2023  润新知