0,,参考
Android 8.0.0 源码
1,问题
通常 在dispatchTouchEvent或onTouchEvent的方法 返回 true时,当前的View 就会按照 「ACTION_DOWN -> ACTION_MOVE -> ACTION_MOVE -> 无数个ACTION_MOVE -> ACTION_CANCEL 或 ACTION_UP」的顺序执行
那么,ACTION_MOVE的触发,是在View中循环触发的,还是通过 DecorView 分发下来的???
2,测试方案
public class BView extends LinearLayout {
public BView(Context context) {
super(context);
}
public BView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
private int start_b = 8;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_MOVE) {
if (start_b-- > 0) {
int a = 10; // 啥也不敢,只是占个位置
} else {
LogUtil.v("start debug"); // 从这里开始debug, 就可以看到调用链,从哪里来的
}
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean defaultResult = super.onTouchEvent(event);
LogUtil.v("defaultResult = " + defaultResult);
return true; // 返回true,当前View处理事件,才会出现Action_Move
}
}
如上图,我们在 dispatchTouchEvent中,加入一个常量去控制。然后,在常量小于0的时候,debug一个断点。这样,就可以排除其它的调用。而是专注于看 ACTION_MOVE 触发的来源。到底是,内部View的递归,还是 DecorView的dispatch
测试结果:
ACTION_MOVE的调用链 是 DecorView中的dispatch,调用链往上找依次是:
BView.dispatchTouchEvent()
LinearLayout.dispatchTouchEvent()
LinearLayout.dispatchTransformedTouchEvent()
FrameLayout.dispatchTouchEvent() // 这个就是熟悉的 android.R.id.content
FrameLayout.dispatchTransformedTouchEvent()
LinearLayout.dispatchTouchEvent()
LinearLayout.dispatchTransformedTouchEvent()
DecorView.dispatchTouchEvent() // 熟悉的DecorView
DecorView.superDispatchTouchEvent()
PhoneWindows.superDispatchTouchEvent() // 熟悉的PhoneWindows
Activity.dispatchTouchEvent() // 熟悉的Activity
DecorView.dispatchTouchEvent()
DecorView.dispatchPointerEvent()
ViewRootImpl.processPointerEvent() // 熟悉的ViewRootImpl
ViewRootImpl.processPointerEvent()
ViewRootImpl.deliver()
ViewRootImpl.onDeliverToNext() // 其实,一般到这里就算结束了,不过还是多贴一些。
...
Choreographer.doCallbacks
Choreographer.doFrame
Choreographer.FrameDisplayEventReceiver.run // 熟悉的人都知道,这里是View绘制的源头
Handler.handleCallback
Handler.dispatchMessage // 这里就是「同步屏障」的分发位置,和一般的Handler也很相似
Looper.loop
ActivityThread.main // 这里就是,Java主线程入口了
RuntimeInit.run
ZygoteInit.main // 这里就是程序的开始了,到这里就彻底没当前进程的调用链了
结论:ACTION_DOWN、ACTION_MOVE、ACTION_UP也好,所有的事件都是从ViewRootImpl那里来的,理解成DecorView开始分发,也是可以的。
题外话 + 闲聊:
带着这个结论,再去看ViewGroup.java 中的 dispatchTouchEvent 的实现,你会发现 里面也是 利用全局变量,去记录状态。然后,处理不同的MotionEvent的不同情况,例如:mFirstTouchTarget。
这里和我们平时写代码基本没啥两样。
这里也在想,其实ViewRootImpl 要求在原始线程中绘制View,其实也是逼不得已。