android的touch事件分发机制-View篇
2015-08-11 13:33
645 查看
转载请注明出处:http://write.blog.csdn.net/mdeditor#!postId=47418837
先赞一下Markdown编辑器,很好用:)
网上讲解touch事件分发的文章已经很多了,看得多了,更觉得混乱。所以打算自己写一篇,顺理一下,也希望对后来者能有点用处。
在具体讲解之前,我想先回答几个常见的问题。
1. 为什么我们要了解touch的分发机制
2. touch的事件分发涉及到哪几个方法
3. view和viewgroup的分发机制一样吗
我们先来看一段代码:
在这段代码里,我声明了一个TextView,然后注册了一个新的touch事件的监听器,运行结果如下:
08-11 16:14:16.380 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=0
08-11 16:14:16.575 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=1
08-11 16:14:16.600 27057-27057/wanmei.com.svgtest E/﹕ OnClickListener
这个比较简单,我想大家都知道:
action=0指的是ACTION_DOWN事件,action=1指的是ACTION_UP事件,因为我点击的比较快,手指也没有移动,所以并没有触发ACTION_MOVE事件(action=2)
而Click事件在touch事件之后执行了
现在,我们把onTouch方法的返回值改成true,输出会怎么样呢:
08-11 16:14:16.380 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=0
08-11 16:14:16.575 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=1
可以看到,在触发了touch事件后,并没有触发Click事件.
这是为啥?
先普及一个问题:在你点击一个view的时候,其实并不是直接就走onTouch了(虽然看起来是你注册了一个touch的监听器并实现了这个接口)。
实际上,在你点击以后,先走的是dispatchPointerEvent这个方法(此方法在view类里可以看到):
这个方法具体干啥用的,有兴趣的童鞋可以研究一下,因为不在咱们这篇的研究范围内,就不赘述了。
但是大家可以看到,这个方法的return值有两种,当event.isTouchEvent()的时候,调用了dispatchTouchEvent方法。
这个dispatchTouchEvent方法才是咱们关心的。它负责当view捕获到touch事件时,分发事件:
代码比较长,但是咱们不需要都看明白,单独看下面这段:
我来解释一下:
首先通过onFilterTouchEventForSecurity判断当前view是否被遮挡,然后定义了一个ListenerInfo的实例。
ListenerInfo这个类是View的内部类,有兴趣的童鞋可以看看源码,里面定义了各种监听器,包括onclick,onlongclick什么的。
然后重点来了,在判断了一堆是不是null,可不可用之后,最终还是调用到了li.mOnTouchListener.onTouch(this, event)
也就是我们在文章最初提到的onTouch方法。
而且,在这里我们还用到这个onTouch的返回值(这里其实用到了4个返回值,li肯定是不为null了,li.mOnTouchListener是不是null取决于你有没有setOnTouchListener,(mViewFlags & ENABLED_MASK) == ENABLED取决于你的这个view控件是不是enable的),只有当onTouch返回true的时候,result才等于true,也就是dispatchTouchEvent才返回true。
那么dispatchTouchEvent返回true or false有什么意义呢?咱们来看源码的注释:
@return True if the event was handled by the view, false otherwise.
当返回true的时候,表明事件已经被view被处理掉了,false的时候反之。
我们来理一下思路:
当你触摸屏幕的时候,view最先做的是分发这个触摸事件,所以不管咋样,进来给dispatchTouchEvent分发,然后呢,在做了一堆判断后,分发给了onTouch。
而onTouch在做了处理之后,返回了一个布尔值。这个布尔值将决定dispatchTouchEvent是否继续分发这个触摸事件。
所以在文章的开头,如果你的onTouch返回了true,明显dispatchTouchEvent就不会再分发给onClick了
ps:dispatchTouchEvent代码里还有一个onTouchEvent,这个方法是activity的,有机会我会写一篇单独说明。
pps:写的比较浅,看到csdn里有很多写的比我深的多的,只希望我这篇对于初学者更易懂一些:)
先赞一下Markdown编辑器,很好用:)
网上讲解touch事件分发的文章已经很多了,看得多了,更觉得混乱。所以打算自己写一篇,顺理一下,也希望对后来者能有点用处。
在具体讲解之前,我想先回答几个常见的问题。
1. 为什么我们要了解touch的分发机制
2. touch的事件分发涉及到哪几个方法
3. view和viewgroup的分发机制一样吗
我们先来看一段代码:
TextView view = (TextView)findViewById(R.id.view_touch); view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.e(null, "OnClickListener"); } }); view.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { Log.e(null, "OnTouchListener action=" + motionEvent.getAction()); return true; } });
在这段代码里,我声明了一个TextView,然后注册了一个新的touch事件的监听器,运行结果如下:
08-11 16:14:16.380 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=0
08-11 16:14:16.575 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=1
08-11 16:14:16.600 27057-27057/wanmei.com.svgtest E/﹕ OnClickListener
这个比较简单,我想大家都知道:
action=0指的是ACTION_DOWN事件,action=1指的是ACTION_UP事件,因为我点击的比较快,手指也没有移动,所以并没有触发ACTION_MOVE事件(action=2)
而Click事件在touch事件之后执行了
现在,我们把onTouch方法的返回值改成true,输出会怎么样呢:
08-11 16:14:16.380 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=0
08-11 16:14:16.575 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=1
可以看到,在触发了touch事件后,并没有触发Click事件.
这是为啥?
先普及一个问题:在你点击一个view的时候,其实并不是直接就走onTouch了(虽然看起来是你注册了一个touch的监听器并实现了这个接口)。
实际上,在你点击以后,先走的是dispatchPointerEvent这个方法(此方法在view类里可以看到):
public final boolean dispatchPointerEvent(MotionEvent event) { if (event.isTouchEvent()) { return dispatchTouchEvent(event); } else { return dispatchGenericMotionEvent(event); } }
这个方法具体干啥用的,有兴趣的童鞋可以研究一下,因为不在咱们这篇的研究范围内,就不赘述了。
但是大家可以看到,这个方法的return值有两种,当event.isTouchEvent()的时候,调用了dispatchTouchEvent方法。
这个dispatchTouchEvent方法才是咱们关心的。它负责当view捕获到touch事件时,分发事件:
public boolean dispatchTouchEvent(MotionEvent event) { // If the event should be handled by accessibility focus first. if (event.isTargetAccessibilityFocus()) { // We don't have focus or no virtual descendant has it, do not handle the event. if (!isAccessibilityFocusedViewOrHost()) { return false; } // We have focus and got the event, then use normal event dispatch. event.setTargetAccessibilityFocus(false); } boolean result = false; if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } final int actionMasked = event.getActionMasked(); if (actionMasked == MotionEvent.ACTION_DOWN) { // Defensive cleanup for new gesture stopNestedScroll(); } 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; } } if (!result && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } // Clean up after nested scrolls if this is the end of a gesture; // also cancel it if we tried an ACTION_DOWN but we didn't want the rest // of the gesture. if (actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL || (actionMasked == MotionEvent.ACTION_DOWN && !result)) { stopNestedScroll(); } return result; }
代码比较长,但是咱们不需要都看明白,单独看下面这段:
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; } }
我来解释一下:
首先通过onFilterTouchEventForSecurity判断当前view是否被遮挡,然后定义了一个ListenerInfo的实例。
ListenerInfo这个类是View的内部类,有兴趣的童鞋可以看看源码,里面定义了各种监听器,包括onclick,onlongclick什么的。
然后重点来了,在判断了一堆是不是null,可不可用之后,最终还是调用到了li.mOnTouchListener.onTouch(this, event)
也就是我们在文章最初提到的onTouch方法。
而且,在这里我们还用到这个onTouch的返回值(这里其实用到了4个返回值,li肯定是不为null了,li.mOnTouchListener是不是null取决于你有没有setOnTouchListener,(mViewFlags & ENABLED_MASK) == ENABLED取决于你的这个view控件是不是enable的),只有当onTouch返回true的时候,result才等于true,也就是dispatchTouchEvent才返回true。
那么dispatchTouchEvent返回true or false有什么意义呢?咱们来看源码的注释:
@return True if the event was handled by the view, false otherwise.
当返回true的时候,表明事件已经被view被处理掉了,false的时候反之。
我们来理一下思路:
当你触摸屏幕的时候,view最先做的是分发这个触摸事件,所以不管咋样,进来给dispatchTouchEvent分发,然后呢,在做了一堆判断后,分发给了onTouch。
而onTouch在做了处理之后,返回了一个布尔值。这个布尔值将决定dispatchTouchEvent是否继续分发这个触摸事件。
所以在文章的开头,如果你的onTouch返回了true,明显dispatchTouchEvent就不会再分发给onClick了
ps:dispatchTouchEvent代码里还有一个onTouchEvent,这个方法是activity的,有机会我会写一篇单独说明。
pps:写的比较浅,看到csdn里有很多写的比我深的多的,只希望我这篇对于初学者更易懂一些:)
相关文章推荐
- android(3)自定义View
- Android安卓开发官方文档国内镜像
- Android整机源码如何轻松下载?清华大学Android源码镜像
- Android如何扩大按钮点击区域
- AndroidMaterialDesign_ClipViews
- Android ListView性能优化
- android反编译-反编译工具和方法
- Android studio安装
- Android 调用百度地图所需权限
- Android javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
- 【Xamarin开发 Android 系列 6】 Android 结构基础(上)
- Android开发 获取当前activity的屏幕截图
- Android反编译获取资源文件-android学习之旅(69)
- Android四大组件之Service
- Android反编译获取资源文件-android学习之旅(69)
- Android反编译获取资源文件-android学习之旅(69)
- Android反编译获取资源文件-android学习之旅(69)
- Android Studio调试技巧
- 文章标题
- Android页面滑动