写在前面 对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; }关于点击事件这一块先说到这里。