Android 触摸反馈流程解析

366 阅读4分钟

点击手机屏幕中的一个按钮,发生了什么?

当手机点击到屏幕中的某个按钮,从手指按下,移动,抬起或取消,会组成一组事件序列,根据这些事件序列,产生一系列的触摸反馈,比如界面滑动,按钮水波纹,界面跳转等等。而这些事件序列是成组产生的,由按下ACTION_DOWN、移动ACTION_MOVE、抬起ACTION_UP或取消ACTION_CANCEL组成。其中由按下为开始,抬起或取消为结束。其中取消操作是非人为的,它是一个比较特殊的事件,在后续文章中会提到。

触摸反馈中的重要方法

Android的触摸反馈由事件分发机制来进行产生,讲到Android事件分发,很多时候面试都会被问到这个,笔者在这个问题上,问过别人,也被别人问过。由此可见掌握它的重要性。 要想掌握事件分发,绕不开的几个方法:

  • dispatchTouchEvent()

dispatchTouchEvent方法在ActivityViewGroupView中都有。

  • Activity中,该方法用于将事件传递到Activity中的PhoneWindow完成,PhoneWindow再把事件传递到整个控件树的根DecorView,之后再由DecorView将事件处理工作交给ViewGroup,当事件没被处理,则继续走onTouchEvent方法
  • ViewGroup中,事件分发从该方法开始,主要逻辑是进行是否拦截的判断,如果拦截走自身的onTouchEvent,如果不拦截则调用dispatchTransformedTouchEvent()方法,这个方法会调用child或者super的dispatchTouchEvent(),最终通过ViewonTouchEvent()/onTouch()等方法的返回值来决定dispatchTransformedTouchEvent()的返回值
  • View中,在这个方法里面会先进行OnTouchListener的判断,判断是不是设置了该监听,再判断当前控件是不是被禁用,在判断onTouch()方法的,如果再这里被处理,则直接返回true,后续的onTouchEvent方法将不会被执行,否则就调用onTouchEvent方法处理
  • onTouchEvent

该方法是消费事件的主要方法,存在于view中,viewGroup默认并没有重写该方法。方法返回true表示消费事件,返回false表示不消费事件。 viewGroup分发事件时,如果没有一个子view消费事件,那么会调用自身的onTouchEvent方法来处理事件。View的dispatchTouchEvent方法中,并不是直接调用onTouchEvent方法来消费事件,而是先调用onTouchListener判断是否消费;如果onTouchListener没有消费事件,才会调用onTouchEvent来处理事件。 我们为view设置的onClickListener与onLongClickListener都是在View的dispatchTouchEvent方法中,根据具体的触摸情况被调用。

  • InterceptTouchEvent

该方法只存在于viewGroup中,当一个事件需要被分发到子view时,viewGroup会调用此方法检查是否要进行拦截。如果拦截则自己处理,而如果不拦截才会调用子view的 dispatchTouchEvent 方法分发事件。 方法返回true表示拦截事件,返回false表示不拦截。

触摸事件序列的顺序

皆不处理不拦截事件

view 流程图1.png

打印日志较长,点击这里展开查看
 
EventActivity-> : dispatchTouchEvent:        
EventLayout-> :dispatchTouchEvent:           
EventLayout-> :onInterceptTouchEvent:        
EventLayout-> :onInterceptTouchEvent: false  
EventView-> :dispatchTouchEvent:             
EventView-> :onTouchEvent:                   
EventView-> :onTouchEvent: false             
EventView-> :dispatchTouchEvent: false       
EventLayout-> :onTouchEvent:                 
EventLayout-> :onTouchEvent: false           
EventLayout-> :dispatchTouchEvent: false     
EventActivity-> : onTouchEvent:              
EventActivity-> : onTouchEvent: false        
EventActivity-> : dispatchTouchEvent: false    
  • 该流程图将手指点击屏幕上面某个view的事件,从开始到结束,进行了展示。可以看到,事件的分发以及处理是一个递归的过程。先不考虑其他拦截情况,不考虑其他的事件传递,单从activityviewGroupview三者之间进行描述。
  • 可以看到当EventActivity收到事件的时候,通过调用dispatchTouchEvent,将事件传递到EventLayout.
  • EventLayout调用dispatchTouchEvent方法,先调用自己的onInterCeptTouchEvent,判断是否拦截事件,如果拦截,则返回true,则调用自己的onTouchEvent方法进行处理,EventView将不会有机会得到该事件序列,图上展示的是不拦截的情况,在此需要读者自己区分一下。继续回到流程图上展示不拦截的处理,当EventLayout不拦截该事件序列后,事件序列会来到EventView.
  • EventView通过调用dispatchTouchEvent将事件传递到自身的onTouchEvent,如果EventViewonTouchEvent在事件ACTION_DOWN的时候不进行拦截,则onTouchEvent返回falseEventViewdispatchTouchEvent将会返回onTouchEvent返回的结果。事件开始回传
  • 事件经过回传,会回到EventLayoutonTouchEventdispatchToucnEvent,EventActivityonTouchEventdispatchTouchEvent。事件结束

致谢

Android事件分发机制三:事件分发工作流程