onTouch和onClick 的那些事儿
原文首发于微信公众号:躬行之(jzman-blog),欢迎关注交流!
事件的分发流程可以说基本上已经阐述清楚,在阅读本篇文章之前,请先阅读下面几篇文章:
还有一个问题是 Android 事件传递过程中 onTouch 和 onClick 事件在整个事件过程中是如何进行事件传递的,下面主要是关于 onTouch 、 onClick 与事件传递过程中调用的先后顺序,将从如下几个方面介绍:
- 源码中的 onTouch() 方法
- 源码中的 onClick() 方法
- onTouch() 与 onClick() 方法之间的关系
- 总结
源码中的onTouch()方法
当要设置触摸事件的监听时,使用到 View 类中的 OnTouchListener 接口,然后通过 setOnClickListener 设置对触摸事件的监听,然后就可以通过具体的事件类型去执行某些操作,onTouch() 方法就是接口 OnTouchListener 中定义的方法,在 View 的 dispatchTouchEvent() 方法中调用,下面时 onTouch方法在源码中的具体调用:
//事件分发
public boolean dispatchTouchEvent(MotionEvent event) {
...
//默认返回值
boolean result = false;
...
//注意判断条件
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
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;
}
上述代码中,先来看一下最外面的条件 onFilterTouchEventForSecurity() 方法,只关心该方法的返回值即可,源码如下:
/**
* 如果事件正常分发返回true,如果事件被丢弃返回false
* @see #getFilterTouchesWhenObscured
*/
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;
}
所以该方法正常情况下返回 true,然后关键的条件主要就是 ListenerInfo 是否为 null,mOnTouchListener 是否为 null,以及 onTouch() 方法的返回值,下面是 ListenerInfo 初始化的源码部分,具体如下:
//getListenerInfo()的具体调用
public void setOnTouchListener(OnTouchListener l) {
getListenerInfo().mOnTouchListener = l;
}
//ListenerInfo的初始化
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
显然,当通过 setOnTouchListener() 方法设置触摸事件的监听时就初始化了 ListenerInfo,同在在设置触摸事件监听的时候 mOnTouchListener != null 成立,最后 onTouch() 方法的返回值决定了 dispatchTouchEvent() 方法是否返回 true。所以,当设置了触摸监听事件且 onTouch() 方法返回 true 时,表示事件就此处理也就不再向子 View 传递了,同时,onTouchEvent() 方法也就不再执行,返回 false 则 onTouchEvent() 方法还会执行。
源码中的onClick()方法
当要设置点击事件的事件监听时,使用到 View 类中的 OnClickListener 接口,然后通过 setOnClickListener 设置对单击事件的监听,然后就可以通过具体的事件类型去执行某些操作,onClick() 方法就是 OnClickListener 接口中定义的方法,在 View 的 onTouchEvent() 方法中调用,在下文中也会进一步得到验证,下面是 onClick() 方法在源码中的具体调用:
//事件处理
public boolean onTouchEvent(MotionEvent event) {
...
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
case MotionEvent.ACTION_UP:
...
//如果执行了该方法,其返回值就是onTouchEvent()的返回值
performClick();
...
break;
}
return true;
}
return false;
}
上面代码中至少找到了 onClick() 方法的调用位置,下面是 performClick() 方法:
/**
* 主要回调了 OnClickListener
*/
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
//调用了OnClickListener接口中的onClick()方法
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
显然,只要我们在代码中通过 setOnClickListener() 方法设置了对单击事件的监听,则对应 View 的 onTouchEvent() 方法返回 true,当然事件就此消费,反之返回 false,那么 onTouch 与 onClick 之间的调用顺序如何,它们之间会互相影响吗,下面就会从案列的角度了解它们之间的关系。
onTouch()与onClick()方法之间的关系
还是之前的案例,MLinearLayout 嵌套 MRelativeLayout,MRelativeLayout 嵌套 MTextView,三个 View 都只是重写了与它们自身相关的事件分发,然后为 MTextView 设置对触摸事件、单击事件的监听,具体如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.textView).setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i("Event", "TextView-------onTouch---------------return:" + false);
return false;
}
});
findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("Event", "TextView-------onClick");
}
});
}
- 让 onTouch() 返回 false,查看日志如下:
结论:设置了对触摸事件的监听,onTouch() 方法 false 时 onTouchEvent() 方法在 onTouch() 方法之后执行,事件就此消费,接着接受 ACTION_DOWN 之后的一系列事件,途中使用鼠标,故没有 ACTION_MOVE 事件,还有在 onTouch() 方法返回 false 的情况下 onClick() 执行了。
- 让 onTouch() 返回 true,查看日志如下:
结论 :当 onTouch() 返回 true 的时候,正如前面所述 onTouchEvent() 将不会再执行,故 onClick() 也就不会再执行。
总结
onTouch() 方法的返回值决定了 onTouchEvent() 方法要不要执行,如果 onTouch() 返回 true,则 onTouchEvent() 不会再执行,返回 false ,则 onTouchEvent() 继续执行,而 onClick() 的回调是在 onTouchEvent() 方法中调用,onTouchEvent() 不执行则 onClick() 不执行。
上一篇: Java并发工具那些事儿
下一篇: 【服务器】iis http跳转https