Android(安卓)浅析View的事件分发机制
对于一个Android开发者来说View事件分发机制是不得不了解的.不了解事件分发机制的话很多自定义控件都无法写出来.今天我们一起来看看View的事件分发机制是如何实现的.
首先来看个栗子:
自定义一个MyButton继承Button并重写dispatchTouchEvent和onTouchEvent方法
@Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: Log.e(TAG, "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(TAG, "dispatchTouchEvent ACTION_UP"); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.e(TAG, "onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(TAG, "onTouchEvent ACTION_UP"); break; } return super.onTouchEvent(event); }
xml里面调用
MyButton mButton= (MyButton) findViewById(R.id.my_bt); mButton.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.e(TAG, "onTouch ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "onTouch ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(TAG, "onTouch ACTION_UP"); break; } return false; } }); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.e(TAG, "onClick"); } }); mButton.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { Log.e(TAG, "onLongClick"); return false; } });
当我们只点击一下的时候日志打出
08-14 11:47:38.173 15712-15712/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_DOWN08-14 11:47:38.173 15712-15712/fun.hxy.com.testalg E/MainActivity: onTouch ACTION_DOWN08-14 11:47:38.174 15712-15712/fun.hxy.com.testalg E/MyButton: onTouchEvent ACTION_DOWN08-14 11:47:38.199 15712-15712/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_UP08-14 11:47:38.199 15712-15712/fun.hxy.com.testalg E/MainActivity: onTouch ACTION_UP08-14 11:47:38.199 15712-15712/fun.hxy.com.testalg E/MyButton: onTouchEvent ACTION_UP08-14 11:47:38.200 15712-15712/fun.hxy.com.testalg E/MainActivity: onClick
我们发现时间都是从dispatchTouchEvent开始的然后onTouch->onTouchEvent->onClick
为什么会这样呢?我们来看一下View的源码:
public boolean dispatchTouchEvent(MotionEvent event) {1 ....2 if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; }3 if (!result && onTouchEvent(event)) { result = true; } ..... return result; }
在View的dispatchTouchEvent里面我们可以发现View先判断li.mOnTouchListener.onTouch(this, event)先执行了onTouch()事件在到第三行的onTouchEvent(),并且如果onTouch()的返回值返回了true的话就不会在往下执行onTouchEvent()方法了;
我们在看看onTouchEvent()的源码:
public boolean onTouchEvent(MotionEvent event) { ...... switch (action) { case MotionEvent.ACTION_UP: if (!focusTaken) { if (!post(mPerformClick)) { performClick(); } } break; } return true; } return false; }
我们发现onClick的方法是在onTouch()方法的up里面执行的.这就是为什么View点击的时候会先调用onTouch() -> onTouchEvent() -> onClick(),面试的时候经常会问到
我们在来看一下长按事件:
现在我们长按button按钮
08-14 14:16:16.725 11084-11084/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_DOWN08-14 14:16:16.726 11084-11084/fun.hxy.com.testalg E/MainActivity: onTouch ACTION_DOWN08-14 14:16:16.726 11084-11084/fun.hxy.com.testalg E/MyButton: onTouchEvent ACTION_DOWN08-14 14:16:16.875 11084-11084/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_MOVE08-14 14:16:16.875 11084-11084/fun.hxy.com.testalg E/MainActivity: onTouch ACTION_MOVE08-14 14:16:16.875 11084-11084/fun.hxy.com.testalg E/MyButton: onTouchEvent ACTION_MOVE08-14 14:16:16.890 11084-11084/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_MOVE08-14 14:16:16.890 11084-11084/fun.hxy.com.testalg E/MainActivity: onTouch ACTION_MOVE08-14 14:16:16.890 11084-11084/fun.hxy.com.testalg E/MyButton: onTouchEvent ACTION_MOVE08-14 14:16:16.906 11084-11084/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_MOVE......08-14 14:16:17.142 11084-11084/fun.hxy.com.testalg E/MainActivity: onLongClick08-14 14:16:17.189 11084-11084/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_MOVE08-14 14:16:17.190 11084-11084/fun.hxy.com.testalg E/MainActivity: onTouch ACTION_MOVE08-14 14:16:17.190 11084-11084/fun.hxy.com.testalg E/MyButton: onTouchEvent ACTION_MOVE.....08-14 14:16:17.948 11084-11084/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_UP08-14 14:16:17.949 11084-11084/fun.hxy.com.testalg E/MainActivity: onTouch ACTION_UP08-14 14:16:17.949 11084-11084/fun.hxy.com.testalg E/MyButton: onTouchEvent ACTION_UP08-14 14:16:17.954 11084-11084/fun.hxy.com.testalg E/MainActivity: onClick
我们发现长按的时候会执行ACTION_DOWN因为手指按住可能会有一点抖,会执行不等次的ACTION_MOVE,然后先执行onLongClick后面放手在执行onClick.
再次翻看onTouchEvent()的源码:
public boolean onTouchEvent(MotionEvent event) { switch (action) { case MotionEvent.ACTION_UP: ...... if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) { if (!post(mPerformClick)) { performClick(); } } ...... break; case MotionEvent.ACTION_DOWN: ...... checkForLongClick(0, x, y); ...... break; } return false; }
我们发现在ACTION_DOWN的时候,系统就会执行checkForLongClick方法
private void checkForLongClick(int delayOffset, float x, float y) { if (mPendingCheckForLongPress == null) { mPendingCheckForLongPress = new CheckForLongPress(); } postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout() - delayOffset); }
这个checkForLongClick里面执行了一个延迟操作延迟五百毫秒时候就会执行CheckForLongPress这个延迟事件
@Override public void run() { if (isPressed() && (mParent != null) && mOriginalWindowAttachCount == mWindowAttachCount) { if (performLongClick(mX, mY)) { mHasPerformedLongPress = true; } } }
这个方法里执行了
public boolean performLongClick(float x, float y) { mLongClickX = x; mLongClickY = y; final boolean handled = performLongClick(); mLongClickX = Float.NaN; mLongClickY = Float.NaN; return handled; }
我们可以看到performLongClick这个方法的返回值为boolean类型,所以当onLongClickListener方法返回值改为true的时候,mHasPerformedLongPress 被置为true,从上面又可以看到只有在mHasPerformedLongPress为false的时候performClick()方法才会执行.
好了,到这里View的事件机制差不多就看完一遍了,View的事件机制还是比较简单的,下一篇我将跟大家一起学习一下ViewGroup的事件机制.希望大家都能有所收获.
更多相关文章
- android 执行monkey指令的方法
- Android(安卓)音乐播放器
- Android(安卓)中onSaveInstanceState()解析
- Android一些基础面试题
- EventBus源码追踪
- 学习笔记_android四种点击事件方法
- Android中父View和子view的点击事件
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用