5.2.1 OnTouchListener注册和使用
1)子类实现View.OnTouchListener,View.OnTouchListener是view的一个接口,
public class Workspace extends SmoothPagedView
implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
Insettable, OnScaleGestureListener {
…
@Override
public boolean onTouch(View v, MotionEvent event) {
}
2)向目的view注册OnTouchListener监听器,把类自身当作监听器
public void onChildViewAdded(View parent, View child) {
CellLayout cl = ((CellLayout) child);
cl.setOnInterceptTouchListener(this);
…
}
3)目的view通知监听器,
private OnTouchListener mInterceptTouchListener;
public void setOnInterceptTouchListener(View.OnTouchListener listener) {
mInterceptTouchListener = listener;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
return true;
}
if (action == MotionEvent.ACTION_DOWN) { setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
}
return false;
}
5.2.2 OnTouchListener栈分析
在launcher里的workspace的onTouch调用栈:
Workspace.onTouch(View, MotionEvent) line: 1506 9)
CellLayout.onInterceptTouchEvent(MotionEvent) line: 904 8)
CellLayout(ViewGroup).dispatchTouchEvent(MotionEvent) line: 2113
Workspace(ViewGroup).dispatchTransformedTouchEvent(MotionEvent, boolean, View, int) line: 2568
Workspace(ViewGroup).dispatchTouchEvent(MotionEvent) line: 2202
Workspace.dispatchTouchEvent(MotionEvent) line: 8001
FrameLayout(ViewGroup).dispatchTransformedTouchEvent(MotionEvent, boolean, View, int) line: 2568
FrameLayout(ViewGroup).dispatchTouchEvent(MotionEvent) line: 2202
AnimationDragLayer(ViewGroup).dispatchTransformedTouchEvent(MotionEvent, boolean, View, int) line: 2568
AnimationDragLayer(ViewGroup).dispatchTouchEvent(MotionEvent) line: 2202
DragLayer(ViewGroup).dispatchTransformedTouchEvent(MotionEvent, boolean, View, int) line: 2568
DragLayer(ViewGroup).dispatchTouchEvent(MotionEvent) line: 2202
FrameLayout(ViewGroup).dispatchTransformedTouchEvent(MotionEvent, boolean, View, int) line: 2568
FrameLayout(ViewGroup).dispatchTouchEvent(MotionEvent) line: 2202
LinearLayout(ViewGroup).dispatchTransformedTouchEvent(MotionEvent, boolean, View, int) line: 2568
LinearLayout(ViewGroup).dispatchTouchEvent(MotionEvent)line: 2202 7)
PhoneWindow$DecorView(ViewGroup).dispatchTransformedTouchEvent(MotionEvent, boolean, View, int) line: 2568
PhoneWindow$DecorView(ViewGroup).dispatchTouchEvent(MotionEvent) line: 2202 6)
PhoneWindow$DecorView.superDispatchTouchEvent(MotionEvent) line: 2442
PhoneWindow.superDispatchTouchEvent(MotionEvent) line: 1751 5)
Launcher(Activity).dispatchTouchEvent(MotionEvent) line: 2828 4)
PhoneWindow$DecorView.dispatchTouchEvent(MotionEvent) line: 2403
PhoneWindow$DecorView(View).dispatchPointerEvent(MotionEvent) line: 9522 3)
ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl$QueuedInputEvent) line: 4247
ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl$QueuedInputEvent) line: 4097
ViewRootImpl$ViewPostImeInputStage(ViewRootImpl$InputStage).deliver(ViewRootImpl$QueuedInputEvent) line: 3643
ViewRootImpl$NativePostImeInputStage(ViewRootImpl$InputStage).onDeliverToNext(ViewRootImpl$QueuedInputEvent) line: 3696
ViewRootImpl$NativePostImeInputStage(ViewRootImpl$InputStage).forward(ViewRootImpl$QueuedInputEvent) line: 3662
ViewRootImpl$NativePostImeInputStage(ViewRootImpl$AsyncInputStage).forward(ViewRootImpl$QueuedInputEvent) line: 3788
ViewRootImpl$NativePostImeInputStage(ViewRootImpl$InputStage).apply(ViewRootImpl$QueuedInputEvent, int) line: 3670
ViewRootImpl$NativePostImeInputStage(ViewRootImpl$AsyncInputStage).apply(ViewRootImpl$QueuedInputEvent, int) line: 3845
ViewRootImpl$NativePostImeInputStage(ViewRootImpl$InputStage).deliver(ViewRootImpl$QueuedInputEvent) line: 3643
ViewRootImpl$EarlyPostImeInputStage(ViewRootImpl$InputStage).onDeliverToNext(ViewRootImpl$QueuedInputEvent) line: 3696
ViewRootImpl$EarlyPostImeInputStage(ViewRootImpl$InputStage).forward(ViewRootImpl$QueuedInputEvent) line: 3662
ViewRootImpl$EarlyPostImeInputStage(ViewRootImpl$InputStage).apply(ViewRootImpl$QueuedInputEvent, int) line: 3670
ViewRootImpl$EarlyPostImeInputStage(ViewRootImpl$InputStage).deliver(ViewRootImpl$QueuedInputEvent) line: 3643
ViewRootImpl.deliverInputEvent(ViewRootImpl$QueuedInputEvent) line: 5939
ViewRootImpl.doProcessInputEvents() line: 5913 2)
ViewRootImpl.enqueueInputEvent(InputEvent, InputEventReceiver, int, boolean) line: 5874
ViewRootImpl$WindowInputEventReceiver.onInputEvent(InputEvent) line: 6042
ViewRootImpl$WindowInputEventReceiver(InputEventReceiver).dispatchInputEvent(int, InputEvent) line: 185
MessageQueue.nativePollOnce(long, int) line: not available [native method] 1)
MessageQueue.next() line: 323
Looper.loop() line: 135
ActivityThread.main(String[]) line: 5524
Method.invoke(Object, Object...) line: not available [native method]
ZygoteInit$MethodAndArgsCaller.run() line: 752
ZygoteInit.main(String[]) line: 642
关键点分析:
1)MessageQueue.nativePollOnce
消息循环,获取到jni层按键发给它的触屏事件,从jni转到java层来处理事件
2)ViewRootImpl.doProcessInputEvents
当前窗口的ViewRootImpl获取到事件,并开始处理
3)PhoneWindow$DecorView(View).dispatchPointerEvent
DecorView作为root的实例引用,开始接受事件进行处理。
4)Launcher(Activity).dispatchTouchEvent
通过cb,跳转到activity
5)--7)PhoneWindow.superDispatchTouchEvent
Activity又将事件传给PhoneWindow,逐步在各级child view里面传递,看谁会消耗事件。如果没有消耗,还会传给Activity进行onTouchEvent处理。Activity这里有两个作用,一是Activity实例可以提前捕获touch事件,做特殊处理,二是可以进行onTouchEvent处理。
8)--9)CellLayout.onInterceptTouchEvent
这里主要实现了应用层面view间的OnTouchListener。
5.2.3 OnTouchEvent栈分析
onTouchEvent被实现的地方很多,调用流程也各不相同,以下举例分析。
在launcher里的workspace里,onTouchEvent 和 OnTouchListener调用栈差异很小,也很微秒(底层是一样的,在栈顶附近略有区别):
Workspace(PagedView).onTouchEvent(MotionEvent) line: 2457
Workspace(View).dispatchTouchEvent(MotionEvent) line: 9302
Workspace(ViewGroup).dispatchTransformedTouchEvent(MotionEvent, boolean, View, int) line: 2562
Workspace(ViewGroup).dispatchTouchEvent(MotionEvent) line: 2245
Workspace.dispatchTouchEvent(MotionEvent) line: 8001
FrameLayout(ViewGroup).dispatchTransformedTouchEvent(MotionEvent, boolean, View, int) line: 2568
Workspace.onTouch(View, MotionEvent) line: 1506
CellLayout.onInterceptTouchEvent(MotionEvent) line: 904
CellLayout(ViewGroup).dispatchTouchEvent(MotionEvent) line: 2113
Workspace(ViewGroup).dispatchTransformedTouchEvent(MotionEvent, boolean, View, int) line: 2568
Workspace(ViewGroup).dispatchTouchEvent(MotionEvent) line: 2202
Workspace.dispatchTouchEvent(MotionEvent) line: 8001
Workspace和CellLayout都是ViewGroup的子类,却一个执行了View的dispatchTouchEvent ,一个执行了ViewGroup的dispatchTouchEvent,onTouchEvent只在View里被调用,所以能执行最终子类的onTouchEvent。
这种流程的区别在(ViewGroup).dispatchTransformedTouchEvent里,标黄的两个if语句决定了流程差异,
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
// Calculate the number of pointers to deliver.
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
// 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);
}
其中,desiredPointerIdBits又根据调用者的流程和参数而不同。在其上级调用中,mFirstTouchTarget是关键因素,标识当前根ViewGoup里,哪些view能接收和处理按键,mFirstTouchTarget链表的元素通过addTouchTarget添加进去。这个流程待后续详细分析。
另,上面讲的onTouchEvent都是view里面的处理,如果onTouchEvent在activity里面,它一般是用来处理触点在控件外的情形的。