今天来研究一下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运行程序进行实验:
返回值 | Log |
---|---|
true | 手指接触view->在view上移动->抬起,一直打印getAction日志 |
false | 手指接触view打印ACTION_DOWN;在view上移动->抬起,无日志打印 |
看起来返回值会对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函数中,因此:
onTouchListener返回值 | onTouchEvent | dispatch返回值 |
---|---|---|
true | 不会处理 | true |
false | 未知 | 未知 |
由我们目前得到的数据,在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 retur