引言
从Android底层开始分析View的事件分发至上层FrameWork。
分两部分来说:
- 触碰屏幕传递事件至当前Activity.
- Activtiy传递事件至触碰到的View 或者 ViewGroup
触碰屏幕传递事件至当前Activity
触摸事件是由Linux内核的一个Input子系统来管理的(InputManager),Linux子系统会在 /dev/input/ 这个路径下创建硬件输入设备节点(这里的硬件设备就是我们的触摸屏了)。当手指触动触摸屏时,硬件设备通过设备节点像内核(其实是InputManager管理)报告事件,InputManager 经过处理将此事件传给 Android系统的一个系统Service,这个Service叫WindowManagerService。之后WindowManagerService会将事件传递到PhoneWindow.
可以参考下图, WindowManagerService连接PhoneWindow的过程.
WindowManagerService调用dispatchPointer()从存放WindowState的z-order顺序列表中找到能接收当前touch事件的 WindowState,通过IWindow代理将此消息发送到IWindow服务端(IWindow.Stub子类),这个IWindow.Stub属于ViewRoot(这个类继承Handler,主要用于连接PhoneWindow和WindowManagerService),所以事件就传到了ViewRoot.dispatchPointer()中.
看下ViewRoot.dispatchPointer method
你可以看到通过用Message将DISPATCH_POINTER事件发送出去,处理事件应该在handleMessage method里.
看下ViewRoot.handleMessage method
看下ViewRoot.deliverPointerEvent method
最终你会发现调用mView.dispatchTouchEvent(event)
(mView是一个PhoneWindow.DecorView对象),PhoneWindow.DecorView继承FrameLayout(FrameLayout继承ViewGroup,ViewGroup继承自View),DecorView里的dispatchTouchEvent方法如下. 这里的Callback的cb其实就是Activity的attach()方法里的设置回调。
看下PhoneWindow.DecorView dispatchTouchEvent method
回调cb就代表Activity,回调会在Activity的onAttach的时候进行设置.
再看cb执行dispatchTouchEvent method.即执行Activity的dispatchTouchEvent,之后Activity会把事件又重新传递到DecorView,然后会调用父类(ViewGroup)的dispatchTouchEvent 将事件传给父类处理。即调用ViewGroup 和 View的事件分发机制。
总算绕回到View ViewGroup的事件分发机制。
View 或者 ViewGroup的事件分发机制
事件的概念
在Android中,事件主要包括点按、长按、拖拽、滑动等,点按又包括单击和双击,另外还包括单指操作和多指操作。所有这些都构成了Android中的事件响应。
事件分为三种:
- 按下(ACTION_DOWN)
- 移动(ACTION_MOVE)
- 抬起(ACTION_UP)
ViewGroup和View的分发
相关函数:
-
ViewGroup
- public boolean dispatchTouchEvent(MotionEvent event)
- public boolean onTouchEvent(MotionEvent event)
- public boolean onInterceptTouchEvent(MotionEvent event)
-
View
- public boolean dispatchTouchEvent(MotionEvent event)
- public boolean onTouchEvent(MotionEvent event)
ViewGroup和View的事件分发是向下传递的,即ViewGroup会一层层向子View分发事件,直到消费事件或者被丢弃。由此可以看出ViewGroup和View相关函数的返回类型都是Boolean,可以直到Boolean类型决定了某一事件是否是继续往下传,还是被拦截了,或是被消费了。
ViewGroup和View的相关函数都接受参数MotionEvent类型的参数,MotionEvent继承于InputEvent,用于标记各种动作事件。之前提到的ACTIONDOWN、ACTIONMOVE、ACTION_UP都是MotinEvent中定义的常量。我们通过MotionEvent传进来的事件类型来判断接收的是哪一种类型的事件。
ViewGroup继承View,即ViewGroup是一个特殊的View,ViewGroup在事件分发中比View多一个函数, onInterceptTouchEvent函数(拦截事件的函数),顾名思义,就是在ViewGroup一层就将事件拦截下来进行处理。
所以一共是三个函数,我们来总结下三个函数的功能:
dispatchTouchEvent方法用于事件的分发,Android中所有的事件都必须经过这个方法的分发,然后决定是自身消费当前事件还是继续往下分发给子控件处理。返回true表示不继续分发,事件没有被消费。返回false则继续往下分发,如果是ViewGroup则分发给onInterceptTouchEvent进行判断是否拦截该事件。
onTouchEvent方法用于事件的处理,返回true表示消费处理当前事件,返回false则不处理,交给子控件进行继续分发。
onInterceptTouchEvent是ViewGroup中才有的方法,View中没有,它的作用是负责事件的拦截,返回true的时候表示拦截当前事件,不继续往下分发,交给自身的onTouchEvent进行处理。返回false则不拦截,继续往下传。这是ViewGroup特有的方法,因为ViewGroup中可能还有子View,而在Android中View中是不能再包含子View的(iOS可以)。
流程简述:
1.ViewGroup执行dispatchTouchEvent进行分发事件,可以进行拦截或者向下分发给子View。如果拦截事件则执行自己的onTouchEvent。
2.子View接受到事件执行dispatchTouchEvent,在这之中如果设置了Listener监听器,则先执行onTouch方法,然后执行onTouchEvent方法。
然后事件被消费结束。
补充如果子View的Listener监听器重写的onTouch方法返回true,则不会继续执行onTouchEvent方法,如果返回false则表示没有消费结束,继续执行onTouchEvent方法。
总结
- 底层将触摸事件传递到上层的Activity,Activity再传递到ViewGroup,ViewGroup拦截或者不拦截,不拦截则传递分发到子View进行消费,如果这个事件一直没被消费则自动被丢弃。