Android View事件分发机制
来源:互联网 发布:c语言abs 编辑:程序博客网 时间:2024/05/29 10:38
背景
在开发中,我们经常需要自定义android组件,而事件的处理是最重要的部分之一,当手指按下,拖动和释放,都经历了什么事件的处理,会达到怎样的效果,当滑动冲突时,我们需要怎么去解决问题,通过对事件分发的了解,我想,对于上面的问题,你都能迎刃而解。在android中主要两种组件,一是原始的View,例如:TextView,Button …… ,一是继承View的ViewGroup,例如:RelativeLayout,LinearLayout …… ,两者还是有些区别的。
View的事件分发
首先来看View的事件分发机制。
1.1 view事件分发图。
1.2 分发过程。
① 在手机屏幕上发生MotionEvent.DOWN 事件时,即手机在屏幕上按下时,底层将触摸事件传递给View的boolean dispatchTouchEvent(MotionEvent ev)方法,所有的事件都在这个方法里面开始进行分发处理。
② dispatchTouchEvent()有boolean类型的返回值,返回 true 则表示在此view中直接消费掉该事件,可以理解为,直接把事件“吃”了,谁也不给,就自己享用,所以它的下一级View是不会收到事件的(PS:此步骤具体运用是发生在ViewGroup中)。
③ dispatchTouchEvent()分发时,先判断该View是否有设置OnTouchListener监听器,如有,则回调OnTouchListener事件的boolean onTouch()方法,该方法默认是返回false,表示不消费该事件,后面手指释放若有设置OnClickListener监听器,则会调用OnClickListener的onClick( );如果onTouch()返回了true,则表示消费该事件,那么上面提到的onClick()就得不到执行。
④ 在分发完onTouch()事件之后,会调用View的onTouchEvent()方法。
1.3 附上view的dispatchTouchEvent()部分源码,API 25
public boolean dispatchTouchEvent(MotionEvent event) { ...... boolean result = false; if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } final int actionMasked = event.getActionMasked(); 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; } } return result; }
这里省略了一些次要代码:
■ 首先声明了布尔型 result = false 变量,该值是dispathTouchEvent()的返回值;
■ 然后判断mInputEventConsistencyVerifier不为空则调用onTouchEvent()方法;
■ 往下看,根据onFilterTouchEventForSecurity()的返回值来执行里面的代码,这个方法返回该事件是否应该派遣,跟底层有关,一般为true;
■ 看里面的代码,有个ListenerInfo对象li,当li != null , li.mOnTouchListener != null , (mViewFlags & ENABLED_MASK) == ENABLED , li.mOnTouchListener.onTouch(this, event) 都为true时,result = true, 看到li.mOnTouchListener.onTouch(this, event))这个方法,这是就是View的OnTouchListener()监听事件的返回值,当实现该监听方法,并且返回true时,上面那段代码就会将result赋值为true;
■ 最后,返回了result,而dispatchTouchEvent()是在dispatchPointerEvent()里面调用的,返回true则表示当前view直接消费该事件 ,false则会继续分发。
ViewGroup事件分发
再来看看ViewGroup的事件分发:
2.1 ViewGroup事件分发图。
2.2 分发过程。
① 在手机屏幕上发生MotionEvent.DOWN 事件时,即手机在屏幕上按下时,底层将触摸事件传递给顶层的View,即继承的ViewGroup的布局Layout,然后事件也是传递到boolean dispatchTouchEvent(MotionEvent ev)方法,所有的事件都在这个方法里面开始进行分发处理;
② dispatchTouchEvent()有boolean类型的返回值,返回 true 则表示在此view中直接消费掉该事件,可以理解为,直接把事件“吃”了,谁也不给,就自己享用,所以它的子View是不会收到事件的;
③ 在dispatchTouchEvent()的分发过程中,首先会调用boolean onInterceptTouchEvent(MotionEvent ev)方法,根据返回值来确定是否拦截事件,即不往下传,返回true则表示拦截该事件,然后直接调用该view的boolean onTouchEvent(MotionEvent event)方法;这个拦截器onInterceptTouchEvent()经常用于滑动冲突的处理。
④ 若在上面的onInterceptTouchEvent()返回false时,即不拦截事件,那么事件将会分发给下一级子View,在源码中可以看到,调用了child.dispatchTouchEvent( )继续进行往下分发;
⑤ 若子View是ViewGroup,则继续走上面的流程;若子view是View,则走View的分发流程,以此递归实现view的分发机制,实现用户与机器的交互。
2.3 附上ViewGroup的dispatchTouchEvent()部分源码 API 25
public boolean dispatchTouchEvent(MotionEvent ev) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action); } else { intercepted = false; } } else { intercepted = true; } if (!canceled && !intercepted) { if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { if (newTouchTarget == null && childrenCount != 0) { for (int i = childrenCount - 1; i >= 0; i--) { if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { ...... break; } } } } } if (mFirstTouchTarget == null) { handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); } else { while (target != null) { if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; } else { if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { handled = true; } } } } } return handled; }
这里省略了一些次要代码:
■ 首先判断mInputEventConsistencyVerifier不为空则调用onTouchEvent()方法;
■ 然后同样的声明了一个变量handled,并初始化值为false,该值也是最终dispatchTouchEvent()的返回值;
■ 接着判断onFilterTouchEventForSecurity()的返回值,该方法是view的方法:
public boolean onFilterTouchEventForSecurity(MotionEvent event) { //noinspection RedundantIfStatement if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0 && (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) { // Window is obscured, drop this touch. return false; } return true; }
看该方法的注释:Filter the touch event to apply security policies. 即过滤触摸事件的应用安全策略,一般值为true;所以事件将继续往下走;
■ 接下来调用了onInterceptTouchEvent()方法,并用intercepted变量来记录是否拦截的Boolean值;
■ 然后判断if (!canceled && !intercepted) 则继续处理事件,通过for循环遍历该viewGroup的子View,调用dispatchTransformedTouchEvent()来进行分发操作,而该方法是view的一个方法:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { if (child == null) { handled = super.dispatchTouchEvent(event); } else { handled = child.dispatchTouchEvent(event); } return handled; } if (newPointerIdBits == oldPointerIdBits) { if (child == null || child.hasIdentityMatrix()) { if (child == null) { handled = super.dispatchTouchEvent(event); } else { handled = child.dispatchTouchEvent(event); } return handled; } } if (child == null) { handled = super.dispatchTouchEvent(transformedEvent); } else { handled = child.dispatchTouchEvent(transformedEvent); } return handled; }
可以看到,通过child.dispatchTouchEvent进行分发;
总结
要想随心所欲的自定义View,事件分发就要玩的遛,通过对事件的处理和拦截,可以很轻松的处理滑动冲突等问题,本文如有不足之处,还望多多指教。
- android View事件分发机制。
- Android View事件分发机制
- android view事件分发机制
- Android View 事件分发机制
- Android:View事件分发机制
- Android事件分发机制-------View
- Android View事件分发机制
- android事件分发机制view
- Android View 事件分发机制
- Android View事件分发机制
- Android View 事件分发机制
- Android View事件分发机制
- Android View事件分发机制
- android事件分发机制 VIew的事件分发机制
- Android 事件分发机制解析之View的事件分发
- Android View 事件分发机制 源码解析
- Android View、ViewGroup 事件分发机制(一)
- Android View、ViewGroup 事件分发机制(二)
- 清园 沉没的Atlantis redis.conf配置详细解析
- WebView控件的使用
- android WebView 和 Html 的相互调用
- spring整合activemq步骤
- CentOS7安装MySQL数据库
- Android View事件分发机制
- 脚本实现自动建立swap分区
- Android 平台下使用 i2c-tools
- Neo4j安装
- 算法--快速排序
- 做BLE阶段参考过的博客
- VPN组网不用愁,即插即用功能的路由设备帮你忙
- 用Swift3实现n*n阶矩阵顺时针输出
- 【表达式求值】中缀表达式转变为后缀表达式