##一,事件传递机制产生原因:
- 1.为什么要有事件分发机制?
因为安卓里的View是树形结构,View可能会重叠在一起,当我们点击的地方有多个View的时候,就出现滑动冲突了,事件应该分发给谁呢?这时候就有了事件分发机制。
##二,事件的传递机制详解
(一) 布局加载过程:
1,,Activity中的setContentView()调用的是getWindow.setContentView(layoutResId0)方法,(getWindow()得到的是一个photoWindow对象).
2,DecorView是Activity的跟View,也是PhotoWindow的内部类,并且继承了Framlayout.
3,DecorView将屏幕氛围2个部分:titleView和contentView,我们平常加载的布局就是ContentView.
(二),三个方法
dispatchTouchEvent(分发事件)
onInterceptTouchEvent(拦截事件)
onTouchEvent(处理触摸事件)
共同点:都接收一个MotionEvent作为参数,返回值类型都是boolean。
1,dispatchTouchEvent()定义在Activity、ViewGroup和View中,控制TouchEvent走向,其目的就是找出哪个View应该处理该TouchEvent,简言来说,就是用于事件的分发。
a,原则:自上而下分发,采用逐层负责制,最先收到的是Activity;
分发之前先判断是否拦截
b,每个ViewGroup每次在做分发的时候,问一问拦截器要不要拦截(也就是问问自己这个事件要不要自己来处理)如果要自己处理那就在onInterceptTouchEvent方法中 return true就会交给自己的onTouchEvent的处理,如果不拦截就是继续往子控件往下传。默认是不会去拦截的,因为子View也需要这个事件,所以onInterceptTouchEvent拦截器return super.onInterceptTouchEvent()和return false是一样的,是不会拦截的,事件会继续往子View的dispatchTouchEvent传递。
2,onInterceptTouchEvent()定义中ViewGroup中,用于拦截事件.返回true表示拦截;
a,view是么有onInterceptTouchEvent()方法的,因为view不能包含view,所以不需要判断是否拦截
3,onTouchEvent(),定义在Activity、ViewGroup和View中,用来处理触摸事件.
a,对于onTouchEvent return false 就比较简单了,它就是不消费事件,并让事件继续往父控件的方向从下往上流动。
- (三),四个触摸事件
Down,move,up,cancel
- (四),拥有能力处理事件传递的3个类
Activity,ViewGroup,View
3.1,View是树形结构的,所以分发就有顺序.事件收集之后最先传给Activity,然后依次向下传递,最后传入到底层的View
Activity->photoWindow-DecorView->ViewGroup->.....->View
- (五),事件的传递过程
ViewGroup的事件传递方法:
- dispatchTouchEvent
- onInterceptTouchEvent
- onTouchEvent
View的事件传递方法:
- View的dispatchTouchEvent
- View的onTouchEvent
当点击事件产生时,Activity会调用dispatchTouchEvent()方法;
当然具体的事物都是讲给PhotoWindow来完成;
PhotoWindow再把事件交给DecorView完成,(DecorView是继承Fraglayout,处于ViewGroup);
所以,最后DecorView会将事件处理工作交给ViewGroup;
1,父容器拿到事件,不拦截,事件就分发给孩子,如果孩子消费事件,事件传递结束
2,父容器拿到事件,不拦截,事件就分发给孩子,如果孩子不消费事件,事件又传递给父容器,看父容器是否消费(onTouchEvent),事件传递结束
3,父容器拿到事件,拦截,事件就不会分发给孩子,交给自己处理(onTouchEvent),事件传递结束
4,如果ACTION_DOWN事件没有view消费,那么后续的事件ACTION_MOVE,ACTION_UP就不会传递进来,如果ACTION_DOWN消费事件了,那必然会以一个Up或Cancel事件结束
- (六),注意:
1,OnTouchListner的优先级高于onTouchEvent,如果OnTouchListner返回true,onTouchEvent就不执行,反之,则会调用
2,如果事件一直没有被消费,最后会传给Activity,如果Activity也不需要就被抛弃。
3,判断事件是否被消费是根据返回值,而不是根据你是否使用了事件。
##,onTouchListener,onTouchEvent和onClick的优先级别
onTouchListener—–>onTouchEvent—>onclick
##优先级别,可以参考一下文章:
https://blog.csdn.net/fenganit/article/details/53750265
https://blog.csdn.net/huiguixian/article/details/22193977
##三,滑动冲突的解决方式:
之前的所有讲解,就像是所有的招式,滑动冲突,就是我们的用武之地。
外部拦截法和内部拦截法
- (一),外部拦截法:就是在ViewGroup里使用onInterceptTouchEvent()拦截
- (二),内部拦截法:在子View的dispatchTouchEvent()里调用 ,这行代码被调用,父类就不会拦截事件
当传入的参数为true时,表示子组件要自己消费这次事件,告诉父组件不要拦截(抢走)这次的事件。
getParent().requestDisallowInterceptTouchEvent(true);
-----------------------外部拦截在代码中的使用---------------------------------
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN :
mDownX = ev.getX();
mDownY = ev.getY();
break;
case MotionEvent.ACTION_MOVE :
mMoveX = ev.getX();
mMoveY = ev.getY();
float dx = mDownX - mMoveX;
float dy = mDownY - mMoveY;
if(dx > dy) {
return true;//只拦截左右滑动
}
break;
}
return false;
}
---------------------内部拦截在代码中的使用-----------------------------------
public boolean dispatchTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastX;
int deltaY = y - mLastY;
if (父容器需要当前点击事件) {
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
mLastX = x;
mLastY = y;
return super.dispatchTouchEvent(ev);
}
##四,总结
View的事件分发机制实际上就是一个经典的责任链模式,
责任链模式:当有多个对象均可以处理同一请求的时候,将这些对象串联成一条链,并沿着这条链传递修改请求,直到有对象处理它为止。