1、android事件的产生
这一部分比较难,需要涉及android整个架构的基础知识,包括linux系统对事件的处理。
这一部分内容来自 android的窗口机制分析------事件处理 。
由于Android是linux内核的,所以它的事件处理也在linux的基础上完成的,因此本文我们从linux 内核往应用这个方向慢慢理清它的处理过程。
linux内核提供了一个Input子系统来实现的,Input子系统会在/dev/input/路径下创建我们硬件输入设备的节点,一般情况下在我们的手机中这些节点是以eventXX来命名的,如event0,event1等等,但是如果是虚拟机的话,我们可以看到一个mice,这个mice代表鼠标设备,这是由于PC需要使用鼠标来模拟触屏。由于这些设备节点是硬件相关的,所以每款设备都是不尽相同的。看到了这些输入的设备节点,我们可能比较困惑这些eventXX到底代表什么含义呢,也就是说到底是什么样的设备创建了这个节点呢?我们可以从/proc/bus/input/devices中读出eventXX相关的硬件设备,这里具体的就不多说了,我们只需要知道android读取事件信息就是从/dev/input/目录下的设备节点中读取出来的,算是android事件处理的起源吧,可以让大家知道按键、触屏等事件是从哪里来的,不是我们的重点。
首先,简而言之的介绍一下android事件传递的流程,按键,触屏等事件是经由WindowManagerService获取,并通过共享内存和管道的方式传递给ViewRoot,ViewRoot再dispatch给Application的View。当有事件从硬件设备输入时,system_server端在检测到事件发生时,通过管道(pipe)通知ViewRoot事件发生,此时ViewRoot再去的内存中读取这个事件信息。
至于android在事件处理上为什么使用共享内存而不是直接使用Binder机制,我的猜测应该是google为了保证事件响应的实时性,因此在选择进程间传递事件的方式中,选择了高的共享内存的方式,由于共享内存在数据管理过程中基本不涉及到内存的数据拷贝,只是在进程读写时涉及到2次数据拷贝,这个是不可避免的数据拷贝,因此这种方式能够很好的保证系统对事件的响应,但是仅仅是共享内存是不够的,因为共享内存的通信方式并不能够通知对方有数据更新,因此android在事件处理过程中加入了另一种进程间通信方式管道(pipe),管道的效率不如共享内存高,会不会影响事件处理的实时性?没关系,每次system_serve通知ViewRoot只是向其传递一个字符,即轻巧有简单,一个字符的多次数据拷贝,我想google还是能够接受的。
好的,了解了一些基本知识后,我们从底层往上层来分析事件的传递过程,这里为了下文便于理解,首先列出整个事件处理的结构图。
具体内容可以去看该博客。
看到这里,读者需要明确,android的事件由此产生,并且最终传递到viewroot。
2、android的事件模型
博客Android中的事件模型为我们指出了android的事件模型,在此对其进行引用。
以前写 android ,对事件的处理没有太深入,只是简单的 onTouchEvent 就 ok 了,现在写的 UI ,很多自定义组件,父 view 和子 view 都需要接收事件,然后处理。如果不弄明白它的事件传递机制,很难拥有好的用户体验。
Touchevent 中,返回值是 true ,则说明消耗掉了这个事件,返回值是 false ,则没有消耗掉,会继续传递下去,这个是最基本的。
在 View 中跟 Touch 相关的事件有 dispatchTouchEvent , interceptTouchEvnet , onTouchEvent 三种。 dispatchTouchEvent 是负责分发事件的,事件从 activity 传递出来之后,最先到达的就是最顶层 view 的 dispatchTouchEvent ,然后它进行分发,如果返回 false ,则交给这个 view 的 interceptTouchEvent 方法来决定是否要拦截这个事件,如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。
如果事件传递到某一层的子 view 的 onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent 来接收。而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”,而且接收不到下一次事件。(我说的一次事件指的是 down 到 up 之间的一系列事件)
我画了个图,见附件。
总结一下,如果这一次事件没有人消耗掉,则系统不会给你下一次事件,因为他会认为你这次的事件阻塞了,没必要给下一次。onTouchEvent如果不消耗的话,会从子view传递到父view。
3、事件传递具体例子
onInterceptTouchEvent()用于处理事件并改变事件的传递方向。处理事件这个不用说了,你在函数内部编写代码处理就可以了。而决定传递方向的是返回值,返回为false时事件会传递给子控件的onInterceptTouchEvent();返回值为true时事件会传递给当前控件的onTouchEvent(),而不在传递给子控件,这就是所谓的Intercept(截断)。
onTouchEvent() 用于处理事件,返回值决定当前控件是否消费(consume)了这个事件。可能你要问是否消费了又区别吗,反正我已经针对事件编写了处理代码?答案是有区别!比如ACTION_MOVE或者ACTION_UP发生的前提是一定曾经发生了ACTION_DOWN,如果你没有消费ACTION_DOWN,那么系统会认为ACTION_DOWN没有发生过,所以ACTION_MOVE或者ACTION_UP就不能被捕获。
本文源地址:http://www.cnblogs.com/rocky_yi/archive/2011/01/21/1941522.html# ,转载请注明出处!
- <?xml version="1.0" encoding="utf-8"?>
- <com.touchstudy.LayoutView1 xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" >
- <com.touchstudy.LayoutView2
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:gravity="center">
- <com.touchstudy.MyTextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/tv"
- android:text="AB"
- android:textSize="40sp"
- android:textStyle="bold"
- android:background="#FFFFFF"
- android:textColor="#0000FF"/>
- </com.touchstudy.LayoutView2>
- </com.touchstudy.LayoutView1>
当某个控件的onInterceptTouchEvent()返回值为true时,就会发生截断,事件被传到当前控件的onTouchEvent()。如我们将LayoutView2的onInterceptTouchEvent()返回值为true,则传递流程变成:
如果我们同时将LayoutView2的onInterceptTouchEvent()和onTouchEvent()设置成true,那么LayoutView2将消费被传递的事件,同时后续事件(如跟着ACTION_DOWN的ACTION_MOVE或者ACTION_UP)会直接传给LayoutView2的onTouchEvent(),不传给其他任何控件的任何函数。同时传递给子空间一个ACTION_CANCEL事件。传递流程变成(图中没有画出ACTION_CANCEL事件):
附SDK给出的说明:
· You will receive the down event here.
· The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle; this means you should implement onTouchEvent() to return true, so you will continue to see the rest of the gesture (instead of looking for a parent view to handle it). Also, by returning true from onTouchEvent(), you will not receive any following events in onInterceptTouchEvent() and all touch processing must happen in onTouchEvent() like normal.
· For as long as you return false from this function, each following event (up to and including the final up) will be delivered first here and then to the target's onTouchEvent().
· If you return true from here, you will not receive any following events: the target view will receive the same event but with the action ACTION_CANCEL, and all further events will be delivered to your onTouchEvent() method and no longer appear here.