android事件分发机制
android事件分发机制:
我们在屏幕上进行一系列的点击/滑动/抬起等动作时所触发的事件,都是在与android的组件进行交互,而几乎所有的组件都是继承于View或ViewGroup,那他们在android应用中是如何被传递的呢?
事件分发离不开三个重要的方法:
public boolean dispatchTouchEvent(MotionEvent event) //事件分发:返回true当前view消耗事件 不再传递事件 false相反
public boolean onTouchEvent(MotionEvent event) //事件的响应返回true 标明事件已经被消耗 不再传递,false相反
public boolean onInterceptTouchEvent(MotionEvent ev) //拦截事件,true标明拦截此事件 false不拦截
View和ViewGroup都有前面两个方法,而ViewGroup独有第三个方法;因为ViewGroup是设计为组件容器,有一些子view,所以需要让事件传递进行拦截,而View只有它自己,所有不需要拦截;需要声明ViewGroup其实也是View的子类,并且重写了View的dispatchTouchEvent方法
View中事件传递:
以在Activity里面添加一个Button为例:
点击事件 —>> MainActivity(dispatchTouchEvent) —>> false传递给view Button;true则会在Acitivity消耗不再传递事件 —>> button组件 -->> dispatchTouchEvent -->> 如果有注册onTouchListener的话,则先执行Touch事件,返回true则不会执行后面的onTouchEvent和ClickListener;false就会继续执行后面的OnTouchEvent,而ClickListener就是在这里面的
以在Activity里面添加一个自定义viewGroup组件为例:
点击事件 —>> MainActivity(dispatchTouchEvent) —>> false传递给view Button;true则会在Acitivity消耗不再传递事件 —>> dispatchTouchEvent -->> onInterceptTouchEvent -->> 如果有子组件就调用子组件的dispatchTouchEvent -->> 子组件onInterceptTouchEvent -->>如果后面没有子组件了就调用onTouchEvent,如果onTouchEvent返回true则消耗此事件,false的话就事件回传,传给父组件的ontouchEvent,直到消耗此事件或者传回到屏幕
注意:
- 在ViewGroup中,如果要自定义是否拦截事件,除了要重写onInterceptTouchEvent方法之外,还要设置ViewGroup的mGroupFlags标志位,调用它的方法requestDisallowInterceptTouchEvent(true)去设置,因为在源码中,做了这样一个判断:
public boolean dispatchTouchEvent(MotionEvent ev) {
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
mGroupFlags必须有FLAG_DISALLOW_INTERCEPT标志
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
}
- ViewGroup在事件传递中,遍历自己的子View虽然是从末尾位置到开头,事件也是从末尾开始的,请看以下部分源码:
1. 在dispatchTouchEvent方法中遍历子View
final ArrayList<View> preorderedList = buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder
? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);
dispatchTransformedTouchEvent将会去执行子view的事件传递
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
省略部分代码
}
}