Android 事件分发机制
Android 事件分发机制
口诀: 3个主体6件事,3个流程负责制
- 三个主体: Activity , ViewGroup, View
- 六件事:dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent,requestDisallowInterceptTouchEvent,onTouchListener,onClickListener
- 三个流程: MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE, MotionEvent.ACTION_UP
- 负责制:
负责到底只要事处理了流程中的ACTION_DOWN的事件的主体return true,后面的整个都要交给它处理
流程图:https://www.jianshu.com/p/e99b5e8bd67b
流程图的解释请参考,参考的说明里面:https://www.jianshu.com/p/e99b5e8bd67b
这里需要注意的:
- 1: activity 如果dispatchTouchEvent不是return的super 那么都会丢弃,不会再往下传递,也不会触发onTouchEvent的事件
- 2:
执行优先级 onTouchListener >onTouchEvent>onClickListener 如果onTouchListener 的回调onTouch里面 返回的是true那么不会执行onTouchEvent
public boolean onTouchEvent(MotionEvent event) {
case MotionEvent.ACTION_UP:
...
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
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)) {
performClickInternal();
}
}
}
...
}
mMyView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("kodulf","View on click listener");
}
});
mMyView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("kodulf","View on touch listener");
return false;
}
});
执行结果:
D/kodulf: part: =============Activity method:[-dispatchTouchEvent -] action:DOWN
D/kodulf: part: -- LinearLayout -- method:[- dispatchTouchEvent -] action:DOWN
D/kodulf: part: -- LinearLayout -- method:[-MotionEvent -] action:DOWN
D/kodulf: part: -- LinearLayout -- method:[- dispatchTouchEvent -] action:DOWN
D/kodulf: part: -- LinearLayout -- method:[-MotionEvent -] action:DOWN
D/kodulf: part: ++++ My View method:[- dispatchTouchEvent -] action:DOWN
D/kodulf: View on touch listener
D/kodulf: part: ++++ My View method:[-onTouchEvent -] action:DOWN
D/kodulf: part: =============Activity method:[-dispatchTouchEvent -] action:UP
D/kodulf: part: -- LinearLayout -- method:[- dispatchTouchEvent -] action:UP
D/kodulf: part: -- LinearLayout -- method:[-MotionEvent -] action:UP
D/kodulf: part: -- LinearLayout -- method:[- dispatchTouchEvent -] action:UP
D/kodulf: part: -- LinearLayout -- method:[-MotionEvent -] action:UP
D/kodulf: part: ++++ My View method:[- dispatchTouchEvent -] action:UP
D/kodulf: View on touch listener
D/kodulf: part: ++++ My View method:[-onTouchEvent -] action:UP
D/kodulf: View on click listener
- 3: requestDisallowInterceptTouchEvent 这个必须是使用getParent().requestDisallowInterceptTouchEvent. 但是它对ACTION_DOWN 是无效的,因为如果没有ACTION_DOWN传递过来的话,它怎么执行呢?
实例
1.0 事件分发的 demo
1.1 ViewPager 和 SlidingPanelLayout 的滑动冲突的解决
- 代码https://github.com/kodulf/SlidePagerLayoutDemo
- 解决思路2个:
- 1: 自定义ViewPager 在onTouchEvent的事件里面执行如下操作:
@Override
public boolean onTouchEvent(MotionEvent ev) {
float rawX = ev.getRawX();
int width = getWidth();
if(rawX >=width/10)
if(ev.getAction() == MotionEvent.ACTION_DOWN)
getParent().requestDisallowInterceptTouchEvent(true);
return super.onTouchEvent(ev);
}
- 2: 也可以自定SlidingPanelLayout 在他的事件拦截里面执行:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
float rawX = ev.getRawX();
int width = getWidth();
if(rawX <width/10||rawX>(width*9)/10)
return super.onInterceptTouchEvent(ev);
else
return false;
}
- 3: 顺便说一下:DrawerLayout 是不需要去写这个的,因为他内部嵌入了