1.事件传递顺序
Activity -> PhoneWindow ->DecorView -> RootView -> ChildView
2.ViewGroup onInterceptTouchEvent 在什么条件下会执行?
if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {
...//省略代码
intercepted = onInterceptTouchEvent(ev);ev.setAction(action);
// restore action in case it was changed
}
即MotionEvent.ACTION_DOWN 或者是 mFirstTouchTarget !=null
down事件 或者 子View处理事件了
如果此时事件不为down (move 或者 up), 子view没有处理事件,则不会再去判断是否拦截事件,直接由viewGroup处理
3.事件在ViewGroup分发过程
Down事件 -> Viewgroup 是否拦截
-> 拦截, mFirstTouchTarget =null ->ViewGroup处理 (后续事件不再向下传递,皆由ViewGroup处理)
-> 不拦截 ->向下分发到子View, 判断子View是否满足分发条件 (1.点击事件是否在子View范围, 2.子View没有播放动画)
->找到满足子View mFirstTouchTarge 赋值 = 子View ->子view dispatchTouchEvent ,到此,事件分发到子view
->未找到满足子View ViewGroup自己处理 onTouchEvent()
4.View对点击事件的处理过程
是否设置OnTouchListener
->是 -> onTouch return
-> true onTouchEvent 不再执行
-> false -> onTouchEvent()
->否 -> onTouchEvent()
在onTouchEvent()中执行onClickListener()
几个点 :
1.view的Long_clickAble 或者clickable 为true 消耗点击事件
2.long_clickable默认false
3. 只要1成立,view为disable状态同样消耗点击事件
5.View滑动冲突处理
(1)外部拦截法
重写父布局onInterceptTouchEvent
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
intercepted = false;
break;
case MotionEvent.ACTION_MOVE:
if (父容器需要当前点击事件){
intercepted = true;
}else {
intercepted = false;
}
break;
case MotionEvent.ACTION_UP:
intercepted = false;
break;
}
return intercepted;
}
需要注意的几点:1.ACTION_DOWN父容器必须返回false,因为如果拦截down事件,后续事件将不会向下传递
2.move事件,需要则拦截,return true
3.up事件 return false
(2)内部拦截法
内部拦截稍微复杂点,分别要重写子控件的dispatchTouchEvent以及父控件的onTouchEvent
//子控件
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
parent.requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
if (父容器需要当前点击事件) {
parent.requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
intercepted = false;
break;
}
return super.dispatchTouchEvent(ev);
}
//父控件
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN){
return false;
}else {
return true;
}
}
内部拦截法是父容器不拦截任何事件,所有事件都传递给子元素,如果子元素需要则消耗掉事件,如果子元素不需要则交给父容器处理