Android view事件分发流程(三)— dispatchTransformedTouchEvent
1、dispatchTransformedTouchEvent介绍
dispatchTransformedTouchEvent(调度转换触摸事件)是在ViewGroup中的方法,主要是确定调用ViewGroup的父类view.dispatchTouchEvent方法还是确定是调用Child.dispatchTouchEvent方法。其实最后都会调用到view.dispatchTouchEvent方法的。
dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits)方法有四个参数,event参数就是事件。cancel参数是非正常取消事件(如子View内部不需要事件,自己调用getParentrequestDisallowInterceptTouchEvent(false)拦截事件时,子View事件就需要结束了,cancel为true)。child为目标子View,为null时调用super.dispatchTouchEvent;不为null时则调用child.dispatchTouchEvent方法。desiredPointerIdBits 参数是每一位都代表一个点,大概是触摸屏幕的一根手指。
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// Canceling motions is a special case. We don't need to perform any transformations
// or filtering. The important part is the action, not the contents.
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
// Calculate the number of pointers to deliver.
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
// If for some reason we ended up in an inconsistent state where it looks like we
// might produce a motion event with no pointers in it, then drop the event.
if (newPointerIdBits == 0) {
return false;
}
// If the number of pointers is the same and we don't need to perform any fancy
// irreversible transformations, then we can reuse the motion event for this
// dispatch as long as we are careful to revert any changes we make.
// Otherwise we need to make a copy.
final MotionEvent transformedEvent;
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
handled = child.dispatchTouchEvent(event);
event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
// Perform any necessary transformations and dispatch.
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
transformedEvent.recycle();
return handled;
}
2、总结
1)cancel为true或者是ACTION_CHANCEL事件时,不需要转化子View坐标,直接调用child.dispatchTouchEvent,下面代码在解决上下左右滑动冲突的时候用到
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
2)newPointerIdBits == oldPointerIdBits 时,需要调用child.dispatchTouchEvent时,需要转化子View坐标
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
handled = child.dispatchTouchEvent(event);
3、newPointerIdBits != oldPointerIdBits 或者 child.hasIdentityMatrix() == false时,需要调用child.dispatchTouchEvent时,也需要转化子View坐标
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);