onTouchListener中的onTouch函数
来源:互联网 发布:网络直播基本设备 编辑:程序博客网 时间:2024/06/05 02:39
今天来研究一下onTouch函数的返回值。
首先在一个ImageView中添加一个监听器:
mImageView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Log.i(TAG, "onTouch " + event.getAction()); return false; }});
代码很简单,我们仅打印了一下event的getAction()
值,然后默认返回了一个false。
在这里返回true或false有何意义呢?SDK文档是这样写的:
True if the listener has consumed the event, false otherwise.
返回true说明监听函数消费了这次touch事件,返回false向上层说明监听没有消费这次事件。
实际文档这样说还是不明白:怎么叫消费,是否消费会造成什么影响?
用实践来检验,分别返回true与false运行程序进行实验:
看起来返回值会对listener的调用产生影响,由于在onTouch中直接打印日志,那么表明返回false则会导致后续产生Touch事件根本不会调用onTouch函数。
这是为何?只能看源码来寻求答案了。
首先由注册onTouchListener方法得知,onTouch肯定是在View类中被调用的。首先查找到listener被注册到何处:
public void setOnTouchListener(OnTouchListener l) { // onTouchListener被赋值到View中getListenerInfo().mOnTouchListener getListenerInfo().mOnTouchListener = l;}
接下来找到mOnTouchListener.onTouch()
这样的语句,在View的dispatchTouchEvent
中:
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { return true;}
在API 23上,dispatchTouchEvent
函数共计51行,而对比API 19,此函数代码仅22行。API 23上源码中诸如stopNestedScroll
函数是API 21才引入的,而onTouch事件的响应在新老版本上保持一致,因此研究API 19的代码就可以了,新特性的处理暂不涉及。
在API 19 中,dispatchTouchEvent的源码如下:
public boolean dispatchTouchEvent(MotionEvent event) { // 根据源码注释,InputEventConsistencyVerifier仅用于Debug if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { return true; } if (onTouchEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false;}
代码很短,可以很快浏览完。其中InputEventConsistencyVerifier
类在源码中注释:
Checks whether a sequence of input events is self-consistent. Logs a description of each problem detected.
When a problem is detected, the event is tainted. This mechanism prevents the same error from being reported multiple times.
可以看到这个类主要是检验输入事件的完整性的,对View类没有实质性影响。
另外一个onFilterTouchEventForSecurity通过源码注释可以判断在应用前台时均会返回true。
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;}
那么对观察现象产生影响的只可能是中间的两个if判断了。
在此结合dispatch函数整理一下点击ImageView时的逻辑,
从上至下先进入我们设置的onTouchListener判断中,再进入View的onTouchEvent函数中,因此:
由我们目前得到的数据,在listener返回false时,后两项确实是未知的。因此还要验证一下View中的onTouchEvent到底会返回什么。
验证的方法不难:创建一个子类继承ImageView,仅覆写onTouchEvent函数,打印super.onTouchEvent
的返回值。
public class MyImageView extends ImageView { private static final String TAG = "MyImageView"; public MyImageView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onTouchEvent(MotionEvent event) { boolean upperOnTouch = super.onTouchEvent(event); Log.i(TAG, "super ImageView onTouchEvent return: " + upperOnTouch); return upperOnTouch; }}
在listener返回false时,运行代码打印log:
06-28 17:26:59.235 3939-3939/com.example.gesturetest I/MyImageView: super ImageView onTouchEvent return: false
也就是说目前为止,View中的onTouchEvent没有对我们listener的返回产生影响(通过查看ImageView可以发现其中并没有覆写View的onTouchEvent方法,因此可以去掉ImageView的影响了)。上面的表可以更新为:
目前为止的总体结论:
可以推断,假设ImageView中onTouch返回true,上层的调用者拿到true后,后续再有事件,就会继续把事件传递给这个ImageView;若ImageView中onTouch返回了false,则上层后续不会再把Touch事件传递给ImageView了。
那么这个上层明显就是ImageView在XML中的上级节点了。假设ImageView布局写在LinearLayout中,那么传递给ImageView事件的上层就是LinearLayout。因此最终需要查看ViewGroup类的源码。
ViewGroup实际继承于View,因此也有dispatchTouchEvent函数,不过已经将其完全覆写。
在ViewGroup中查找子ViewdispatchTouchEvent
函数的调用,发现是在dispatchTransformedTouchEvent
函数中,ViewGroup需要将接收到的事件进行转换(例如坐标);再进一步查找dispatchTransformedTouchEvent
的调用,发现就是在ViewGroup的dispatchTouchEvent
中。
整体流程大致为:
在这里推测是dispatchTouchEvent
函数中的逻辑进行了处理。dispatchTransformedTouchEvent
函数声明是:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits)
因此调用dispatchTransformedTouchEvent
是需要指定View的。如果是在这个函数中处理,当我们的ImageView不再接收任何Touch事件时,每次Touch事件都需要调用dispatchTransformedTouchEvent
,效率就会降低。从这个角度,猜测真正是在dispatchTouchEvent
中进行了处理。
接下来整理ViewGroup中dispatchTouchEvent
的逻辑,首先会判断这个事件是否是一个ACTION_DOWN,如果是ACTION_DOWN,表明用户刚刚点击屏幕,是一系列Touch事件的起始,因此会调用cancelAndClearTouchTargets
与resetTouchState
复位状态。
// Handle an initial down.if (actionMasked == MotionEvent.ACTION_DOWN) { // Throw away all previous state when starting a new touch gesture. // The framework may have dropped the up or cancel event for the previous gesture // due to an app switch, ANR, or some other state change. cancelAndClearTouchTargets(ev); resetTouchState();}
再接下来处理ViewGroup拦截Touch事件的逻辑:
// Check for interception.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); // restore action in case it was changed } else { intercepted = false; }} else { // There are no touch targets and this action is not an initial down // so this view group continues to intercept touches. intercepted = true;}
整个流程大致为:
- onTouchListener中的onTouch函数
- 关于OnTouchListener中的onTouch()触发的不同事件说明
- onTouchListener和onTouch的优先级
- 【onTouchEvent()方法】和【OnTouchListener中onTouch()方法】
- 【onTouchEvent()方法】和【OnTouchListener中onTouch()方法】
- 【onTouchEvent()方法】和【OnTouchListener中onTouch()方法】
- 【onTouchEvent()方法】和【OnTouchListener中onTouch()方法】
- 【onTouchEvent()方法】和【OnTouchListener中onTouch()方法】
- Android OnTouchListener自定义 onTouch完全解析
- Android开发中OnTouchListener接口方法onTouch的返回值问题 /不响应TouchListener中的UP和MOVE
- Android学习记录:MotionEvent,onTouch,OnTouchListener 事件机制等学习
- Android之onTouchEvent和OnTouchListener中onTouch的区别
- OnTouch三种OnClickListener、OnTouchListener、OnLongClickListener的学习
- Android中的onTouch事件
- MapView的OnTouchListener与setBuiltInZoomControls冲突导致无法触发OnTouch的解决办法
- OnTouchListener()的onTouch(View v, MotionEvent event)事件以及按键onKeyDown()事件
- setOnTouchListener中的onTouch返回false
- ontouch
- Android知识点总结(知识点交汇点)
- [C++]在Visual Studio 2010中使用Google Test - 配置
- Mass Update Item Attribute using interface
- Android ScrollView滚动 + ListView无滚动条加载全部内容
- 51Nod - 1459 迷宫游戏 dijkstra拓展
- onTouchListener中的onTouch函数
- iOS原生App与H5页面交互笔记
- 在极光推送成功后的情况下实现跳转
- JS中的call()和apply()方法
- Java 权限框架 Shiro 实战二:与spring集成、filter机制,shirofilter
- 一念永恒 > 第130章 育兽花开
- 36个Android开发常用代码片段
- 模板
- 《神秘的程序员们》漫画48~49: 那些年你面试过的奇葩