Android事件分发
view的事件分发之onTouch和onClick
项目中我们经常会遇到对一个控件重写两个方法setOnClickListener和setOnTouchListener来监听这个控件的点击事件和动作,但是如果对于一个控件的这两个方法同时监听,会怎样去执行呢?
->我们先对默认可以点击的控件,如Button进行分析。
我们在一个activity中放一个Button控件,然后设置监听
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(tag, "onClick");
}
});
button.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i(tag, "viewbutton-onTouch-ACTION_DOWN...");
break;
case MotionEvent.ACTION_UP:
Log.i(tag, "viewbutton-onTouch-ACTION_UP...");
break;
default:
break;
}
return false;
}
}
);
点击此控件的话,是onTouch先执行,然后执行onClick,如果onTouch返回true,那么只执行onClick。
我们从dispatchTouchEvent源码得知
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null &&
// 监听事件不为空
li.mOnTouchListener != null
// enable为true
&& (mViewFlags & ENABLED_MASK) == ENABLED
// 这里会先执行
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
在我标注的地方会先执行,即 onTouch(this, event),如果返回true的话,就不会执行下面的判断条件,就是不执行onTouchEvent(event)。但是为什么不执行onClick呢?
我们在查看onTouchEvent(event)的源码的时候会发现
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
而在performClick()中有:
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
就是执行onClick。所以我们的流程就是
先调用控件的dispatchTouchEvent(),然后执行onTouch(),如果onTouch()返回false的话,执行控件的onTouchEvent方法,继而执行onClick方法。
->我们现在对默认不可点击的事件进行分析,如ImageView:
执行顺序其实和上面是一样的,但是在onTouchEvent方法中,有这么一个判断条件:
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
先判断时候可以点击,如果不能就不会进入,然后就不会执行onClick方法。如果想让此种view能执行点击事件,可以设置setClickable(true);