View事件的传递之二----在屏幕上点击时事件在整个系统中的传递流程初分析

来源:互联网 发布:淘宝客手机 编辑:程序博客网 时间:2024/06/10 20:08

这个内容距离上一次隔得有点远,连我自己都有点忘记了,

无奈公司的电脑系统又挂了,今天重装完后发现此前准备的都没了。

于是只能自己再来写一遍打log分析,也算加深印象。

顺便说一句,以前不知道为什么给每位coder配一台mac也算公司福利,

在用过了敝司"超强"的电脑后,我算明白了。

    废话不说,下面我们就来说一下今天需要研究的事件传递的方向。

   今天主要研究的就是Touch事件在android系统中的传递路线。

也就是用户手指按下屏幕后,在整个操作系统内部是如何传递的。

比如说按下了button,

 1.那么这个触摸事件就直接传递给了Button么,还是中间经历了一些波折?

 2.有没有可能按下button后事件不传递给Button?如果可能怎么做到?

 3.几个控件叠在一起的情况下,按在那个位置,到底事件是默认如何传递的?

    我们如果想按照自己的意愿传递那该怎么办?

废话不多说,我们先写一个测试代码,根据测试代码中输出的Log来进行分析,推理。

  我们今天的测试模型因为要考虑事件在整个系统的传递,那么事件一定会传到一些我们需要的控件,那么当事件传递到某些控件时,这些控件自身肯定也会有一些方法被调用,因此为了看清楚,我们就需要自定义一些控件,进行log的输出查看。

  我准备自定义三个控件:

                  OutLinearLayout.java--->最外层的布局view。占满整个屏幕

                  InsideButton.java--->自定义的button

                  InnerLinearLayout.java-->里层的布局view,和button平行放置在最外层。

给InsideButton,和InnerLinearLayout都复写一些事件传递相关的方法,不绑定监听器先,绑定的活动我们上一次分析过。

那先上代码吧:

  布局文件:activity_main.xml:

<com.example.testtouch.OutLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    android:gravity="center"    tools:context=".MainActivity" >    <!-- 为了能看的出来所以给它加了背景颜色为红色 -->    <com.example.testtouch.InnerLinearLayout        android:layout_width="50dp"   android:layout_height="50dp"   android:background="#ff0000"        />        <com.example.testtouch.InnerButton        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="自定义button" /></com.example.testtouch.OutLinearLayout>

  

  OutLinearLayout.java

/** * 最外层的linearlayout * 复写了 * onTouchEvent() *  dispatchTouchEvent(); *  onInterceptTouchEvent(); * @author szxgg * */public class OutLinearLayout extends LinearLayout{public OutLinearLayout(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {Log.d("Touch", "OutLinearLayout..dispatchTouchEvent");return super.dispatchTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.d("Touch", "OutLinearLayout..onTouchEvent");return super.onTouchEvent(event);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {Log.d("Touch", "OutLinearLayout..onInterceptTouchEvent..return is "+super.onInterceptTouchEvent(ev));return super.onInterceptTouchEvent(ev);}}


InnerButton.java

** * 自定义的button * 复写了onTouchEvent(); *    dispatchTouchEvent(); * @author szxgg * */public class InnerButton extends Button{public InnerButton(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.d("Touch", "InnerButton...onTouchEvent..");return super.onTouchEvent(event);}@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {Log.d("Touch", "InnerButton...dispatchTouchEvent..");return super.dispatchTouchEvent(event);}}


 

InnerLinearLayout.java

package com.example.testtouch;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.widget.LinearLayout;/** * 里层自定义的Layout * 重写了 * onTouchEvent() *      dispatchTouchEvent() *      onInterceptTouchEvent(); * @author szxgg  */public class InnerLinearLayout extends LinearLayout{public InnerLinearLayout(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.d("Touch", "InnerLinearLayout...onTouchEvent");return super.onTouchEvent(event);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {Log.d("Touch", "InnerLinearLayout...onInterceptTouchEvent");return super.onInterceptTouchEvent(ev);}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {Log.d("Touch", "InnerLinearLayout...dispatchTouchEvent");return super.dispatchTouchEvent(ev);}}

mainActivity中什么都没有写,将程序运行起来。

 

下面看Log

单独点击空白区域:

11-17 18:54:25.217: D/Touch(7201): OutLinearLayout..dispatchTouchEvent
11-17 18:54:25.217: D/Touch(7201): OutLinearLayout..onInterceptTouchEvent..return is false
11-17 18:54:25.218: D/Touch(7201): OutLinearLayout..onTouchEvent

分析:点击空白区域,事件触发了最外层的layout,所以触发了最外层layout的方法。

执行顺序是:dispatchTouchEvent-->onInterceptTouchEvent(return false)-->onTouchEvent

 

点击button:

11-17 18:54:59.224: D/Touch(7201): OutLinearLayout..dispatchTouchEvent
11-17 18:54:59.224: D/Touch(7201): OutLinearLayout..onInterceptTouchEvent..return is false
11-17 18:54:59.224: D/Touch(7201): InnerButton...dispatchTouchEvent..
11-17 18:54:59.224: D/Touch(7201): InnerButton...onTouchEvent..
11-17 18:54:59.241: D/Touch(7201): OutLinearLayout..dispatchTouchEvent

11-17 18:54:59.241: D/Touch(7201): OutLinearLayout..onInterceptTouchEvent..return is false
11-17 18:54:59.241: D/Touch(7201): InnerButton...dispatchTouchEvent..
11-17 18:54:59.241: D/Touch(7201): InnerButton...onTouchEvent..
11-17 18:54:59.252: D/Touch(7201): OutLinearLayout..dispatchTouchEvent
11-17 18:54:59.253: D/Touch(7201): OutLinearLayout..onInterceptTouchEvent..return is false
11-17 18:54:59.253: D/Touch(7201): InnerButton...dispatchTouchEvent..
11-17 18:54:59.253: D/Touch(7201): InnerButton...onTouchEvent..
11-17 18:54:59.253: D/Touch(7201): OutLinearLayout..dispatchTouchEvent
11-17 18:54:59.253: D/Touch(7201): OutLinearLayout..onInterceptTouchEvent..return is false
11-17 18:54:59.253: D/Touch(7201): InnerButton...dispatchTouchEvent..
11-17 18:54:59.254: D/Touch(7201): InnerButton...onTouchEvent..

 

点击InnerLinearLayout:

11-17 18:55:17.037: D/Touch(7201): OutLinearLayout..dispatchTouchEvent
11-17 18:55:17.037: D/Touch(7201): OutLinearLayout..onInterceptTouchEvent..return is false
11-17 18:55:17.037: D/Touch(7201): InnerLinearLayout...dispatchTouchEvent
11-17 18:55:17.037: D/Touch(7201): InnerLinearLayout...onInterceptTouchEvent
11-17 18:55:17.038: D/Touch(7201): InnerLinearLayout...onTouchEvent
11-17 18:55:17.038: D/Touch(7201): OutLinearLayout..onTouchEvent

点击button和点击innerLayout有什么区别?

  不考虑Log打印的次数,单看事件的执行顺序我们会发现呈现的规律如上红色的标注:

    button在执行完onTouchEvent后好像就结束了,

   就开始下一次的OutLayout的dispatch方法了

   而innerLayout执行完onTouchEvent方法后,还没有结束,

  又调回到了outLinearLayout的onTouchEvent()方法。

我们后面会分析这些现象的原因。

 

首先根据log来提取一些共性的地方:

当一个事件在屏幕上时,比如说按在button上,事件并不是直接先到button的,而是先由外部慢慢的传到内部。

流程:

        activity.dispatchTouchEvent()--最外层的事件处理--第二层的事件处理--最里层的事件处理--activity.onTouchEvent();

如果事件在这个环节中没有被任何控件所拦截,那么它的处理流程就是这样的。

 

我们现在再单分析一层事件是如何处理的:

这个就是最外层的outLinearLayout的log:

   dispatchTouchEvent()--onInterceptTouchEvent()--onTouchEvent();

dispatchTouchEvent()-->对事件进行分发,在activity.java中也有这个方法,将事件分到了rootView

 

onInterceptTouchEvent()-->是否拦截该事件,返回true就是拦截,

如果拦截事件就不在像下分发了,而是直接执行完自己的onTouchEvent()就结束。

如果不拦截就先不调用自己的onTouchEvent,

而是将事件分到下一层(如果有下一层的话,如果没有下一层就还是执行自己的OnTouchEvent());

 

 

onTouchEvent()--执行完了自己的触摸事件。

返回true的话事件到此为止,也就不在像上返回了,如果为false,事件还会像上返回。

 

所以我们了解了三个方法的主要作用:

dispatch是用来分发的,intercept用来判断是不是像下传递的,注意:即使当前view拦截了也不代表这个view

就处理这个事件,而是不是处理这个事件是由onTouchEvent的返回值来确定的。

对于可被点击的控件,onTouchEvent父类中天生返回true.

 

如果一个事件在一次触发中被拦截后,那么在这次触发的其他时间都不会再询问是否需要拦截,而都是被默认当做拦截来看待,一次触发是指手指头按下到抬起的一个过程。

     

 

因此在3层结构中我们如何让第二层响应这个事件?

那就是首先要拦截(拦截是viewGroup才有的),拦截下来后还要消费这个事件,就是onIntercept和onTouch都要返回true.可是在onTouch中不仅有返回的处理,还有包括对onClick的处理,因此不能直接这么复写,而是将那个需要消费的view中的clickable属性设为true也是可以的。



 流程图如下:


 以上是我们基于log推断出来的,那么是不是真的这么走的呢,

这个要看源码,源码分析我们下次看,非常复杂。

Touch事件光记住这些概念远远不够,在写代码的时候一定要多用多思考,才能好好掌握!




 

 

0 0
原创粉丝点击