写在前面
对Android Event事件做一个详细整理。
测试验证:
一:在Activity中只有一个View控件,给View设置了onClickListener和onTouchListener事件
test.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("TAG","onClick");
}
});
test.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i("TAG","onTouch ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i("TAG","onTouch ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i("TAG","onTouch ACTION_UP");
break;
}
return false;
}
});
接下来对View 进行点击测试,到底是View的点击事件先执行还是onTouch先执行:
08-05 10:07:54.010 19588-19588/com.cms.testevent I/TAG: onTouch ACTION_DOWN
08-05 10:07:54.070 19588-19588/com.cms.testevent I/TAG: onTouch ACTION_UP
08-05 10:07:54.080 19588-19588/com.cms.testevent I/TAG: onClick
这是在点击View之后马上抬起,可以看到是先执行onTouch事件之后才到onClick事件。如果在按下View的时候手指稍微移动后在立马抬起会执行Action_Move事件,如下:
08-05 10:12:03.810 19588-19588/com.cms.testevent I/TAG: onTouch ACTION_DOWN
08-05 10:12:03.860 19588-19588/com.cms.testevent I/TAG: onTouch ACTION_MOVE
08-05 10:12:03.870 19588-19588/com.cms.testevent I/TAG: onTouch ACTION_UP
08-05 10:12:03.870 19588-19588/com.cms.testevent I/TAG: onClick
注意到onTouch有返回值,默认返回false,那这个返回值有什么影响呢?我们进行测试:
08-05 10:19:06.290 5068-5068/com.cms.testevent I/TAG: onTouch ACTION_DOWN
08-05 10:19:06.390 5068-5068/com.cms.testevent I/TAG: onTouch ACTION_MOVE
08-05 10:19:06.400 5068-5068/com.cms.testevent I/TAG: onTouch ACTION_MOVE
08-05 10:19:06.410 5068-5068/com.cms.testevent I/TAG: onTouch ACTION_UP
测试发现onClick不在执行了,那这又是怎么回事呢?我们从源码中去寻找答案,找到View的dispatchTouchEvent方法。
public boolean dispatchTouchEvent(MotionEvent event) {
....
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//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;
}
}
....
return result;
}
看关键代码,在第10行到14行,判断了li是否为空和li.onTouchListener是否为空,这个onTouchListener是什么呢?找到onTouchListerner发现就是前面设置的setOnTouchListener()
public void setOnTouchListener(OnTouchListener l) {
getListenerInfo().mOnTouchListener = l;
}
之后在判断View是否是Enable的,默认是Enable的,然后调用了onTouchListener的onTouch方法,如果返回true,那么4个条件全部满足,result为true,如果result为true那么接下来的onTouchEvent方法也就不在执行了,这也就验证了为什么onTouch返回true onClick事件就不在执行的原因。
接下来我们看下onTouchEvent的源码,只有在View是可点击的情况下才会执行事件,前面设置了setOnClickListener所以会执行点击事件,点击事件是在performClick()里面执行的。
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:
...
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
}
...
break;
case MotionEvent.ACTION_DOWN:
...
break;
case MotionEvent.ACTION_CANCEL:
...
break;
case MotionEvent.ACTION_MOVE:
...
break;
}
return true;
}
return false;
}
点击事件在performClick()里被执行。
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
关于点击事件这一块先说到这里。