在前面的小节中,我们以按键的实例,详细的讲解InputStage,该小节多点触摸屏的InputStage,看看Reader线程是如何处理他的,在这之前先来回顾一下之前的知识。
我们知道一个应用程序APP有多个activity,每个activity对应一个window,在window中存在Decorview,我们可以从其中划出一块区域,然后自由创作,比如在上面增加TextView,Button等等。下面是之前我们学习过的理论流程,如果不记得了,可以看看前面的小节: 为了方便的阅读源代码,下面是一个思维导图:
从图中的左下角我们可以找到:
我们从触摸屏的ViewPostImeInputStage处开始分析,即输入法之后的处理。对于触摸屏,在输入法之前,他没有做任何事情,所以我们不用关心。打开源码文件ViewRootImpl.java。 如,我们可以看到以下代码,在输入法之前:
final class ViewPreImeInputStage extends InputStage {
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
}
return FORWARD;
}
这里我们可以知道,输入法之前,如果是触摸屏事件,直接返回FORWARD,他只处理按键类事件。所以我们只关心输入法之后的处理。假设我们有一个应用程序,其界面如下: 如果我们点击界面的button1,他是怎么找到的呢? 1.根据触点的x,y坐标,递归的找到目标控件:先把事件发送给Layout1,看看是否位于其上,如果不是则发送给Layout2,直到其位于Layout3,与Layout3匹配之后在发送给其中的button1,但是其不位于button1,就会发送的button2,这样就找到了这个button。 2.btton如何处理这个事件呢?一般分为两种,click(点击:松开后处理),touch(触摸:按下,松开,滑动都可以处理)。
我们利用取巧的办法,在这两个函数中,添加栈的打印信息。这样我们就能知道其调用过程了。下面是官方文档的一些介绍: 从上面可以看到,onClick属于View,我们打开View.java搜索onClick,可以找到如下:
public interface OnClickListener {
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
void onClick(View v);
}
可以知道onClick被OnClickListener 调用,OnClickListener 又为一个接口,我们找到其定义:
public OnClickListener mOnClickListener;
可知每个View中都存在一个OnClickListener,我们需要去设置mOnClickListener:
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
同样也存在setOnClickListener
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
上面是一个相关知识的简述,下面我们开始编写APP。
APP编写我们在原来的APP_0009_Inputstage工程工程上进行修改,赋值重命名为APP_0009_Inputstage-v2,使用AS打开工程,原来MainActivity.java中存在:
class MyButtonListener implements View.OnClickListener {
@Override
public void onClick(View view) {
View decorView = getWindow().getDecorView();
printViewHierarchy(decorView,0,-1);
}
}
在我们点击button然后松开的时候就会调用onClick函数,现在我们添加onTouch并且修改onClick:
/*在public class MainActivity extends Activity中添加如下*/
class MyButtonListener implements View.OnClickListener {
@Override
public void onClick(View view) {
+ /* View decorView = getWindow().getDecorView();
+ printViewHierarchy(decorView,0,-1);
+ */
+ Log.d(TAG,"***MyButtonListener onClick call ****");
+ Log.d(TAG,Log.getStackTraceString(new Throwable()));
}
}
+ class MyButtonTotchListener implements View.OnTouchListener{
+
+ @Override
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ Log.d(TAG,"***MyButtonListener onToutch call ****");
+ Log.d(TAG,Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
/*在protected void onCreate(Bundle savedInstanceState中添加*/
button.setOnClickListener(new MyButtonListener());
+ button.setOnTouchListener(new MyButtonTotchListener());
然后编译运行APP,点击按钮,我们就能看到onTouch,与onClick的堆栈信息l了,如下:
D/LedDemo: ***MyButtonListener onToutch call ****
2019-03-23 10:54:02.305 1582-1582/com.example.administrator.app_0001_leddemp D/LedDemo: java.lang.Throwable
at com.example.administrator.app_0001_leddemp.MainActivity$MyButtonTotchListener.onTouch(MainActivity.java:76)
at android.view.View.dispatchTouchEvent(View.java:10019)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2264)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2264)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2264)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2264)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2264)
at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:545)
at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1811)
at android.app.Activity.dispatchTouchEvent(Activity.java:3091)
at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:507)
at android.view.View.dispatchPointerEvent(View.java:10243)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4438)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4306)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3906)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3872)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3999)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3880)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4056)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3906)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3872)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3880)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6247)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6221)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6182)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6350)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:323)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:6141)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:912)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:802)
2019-03-23 10:54:02.377 1582-1582/com.example.administrator.app_0001_leddemp
D/LedDemo: ***MyButtonListener onClick call ****
2019-03-23 10:54:02.378 1582-1582/com.example.administrator.app_0001_leddemp D/LedDemo: java.lang.Throwable
at com.example.administrator.app_0001_leddemp.MainActivity$MyButtonListener.onClick(MainActivity.java:67)
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22445)
at android.os.Handler.handleCallback(Handler.java:755)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6141)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:912)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:802)
我们可以看到:
ViewPostImeInputStage.onProcess(View.java:10243)
ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4438)
下面我们打开ViewRootImpl.java文件,找到ViewPostImeInputStage,中的:
protected int onProcess(QueuedInputEvent q) {
processPointerEvent(q);/*对于多点触摸屏会调用该函数*/
boolean handled = eventTarget.dispatchPointerEvent(event)
当调用到dispatchPointerEvent时,我们发现其在PhoneWindow.java文件中没有被实现,查看那么他就会调用父类的dispatchPointerEvent,那我们DecorView的父类FrameLayout,他也没有实现,继续找到ViewGroup.java,依旧没有实现,知道找到View.java,其中实现了dispatchPointerEvent,这也就是说,dispatchPointerEvent最终调用的是View.java中的dispatchPointerEvent:
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
从堆栈打印:
at android.app.Activity.dispatchTouchEvent(Activity.java:3091)
at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:545)
at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:507)
at android.view.View.dispatchPointerEvent(View.java:10243)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4438)
我们也能分析出相同的流程,最终传递给我们的Activity,我们进入Activity.java文件查看,搜索dispatchTouchEvent:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
其处理流程,就与我们之前总结的: 的流程是一样的了,Activity传递触摸屏事件给wind不能处理传递给DecorView,最后传递我们的焦点,即按钮上。我上面我们可以看到dispatchTouchEvent函数调用了superDispatchTouchEvent,该实现为:
at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1811)
我们在PhoneWindow.java中找到:
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
可以看到直接调用的mDecor(DecorView)中的superDispatchTouchEvent函数,在DecorView.java中我们找到:
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
其直接调用父类的dispatchTouchEvent方法,他的父类为没有实现,最终到:
ViewGroup.dispatchTouchEvent(ViewGroup.java:2264)
ViewGroup为组View的意思,代表一个View可能放有多个控件: 在前面的框图中,DecrView,Layout1,Layout12,Layout3都是ViewGroup。ViewGroup怎么处理触摸事件呢?他会判断输入事件位于哪个child上面,即ViewGroup内部的控件,然后调用child中的dispatchTouchEvent:
/*ViewGroup.java*/
public boolean dispatchTouchEvent(MotionEvent ev) {
/*根据触摸点,得到x,y坐标*/
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
/*判断其坐标是否在其内部的child上*/
isTransformedTouchPointInView(x, y, child, null)
/*如果找到了则获得这个child*/
newTouchTarget = getTouchTarget(child);
/*发送转换过的位置信息给child*/
dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)
handled = child.dispatchTouchEvent(event);
从:
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2264)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2264)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2264)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2264)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2264)
中可以看出,其是一个递归的过程。最终会找到我们的button,把输入事件发送给button,调用button的dispatchTransformedTouchEvent函数,但是我们的按键没有实现该函数,最终调用其父类
View.dispatchTouchEvent(View.java:10019)
限制我们看看View.java中的dispatchTouchEvent如何处理这个输入事件:
public boolean dispatchTouchEvent(MotionEvent event) {
/*如果前面我们设置了mOnTouchListener ,这onTouch函数会被调用*/
if (li != null && li.mOnTouchListener != null&& (mViewFlags &ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {
/*事情处理完成,不会在外下进行传递了*/
result = true;
if (!result && onTouchEvent(event)) {
result = true;
如果我们没有设置mOnTouchListener ,则该函数继续往下执行onTouchEvent:
if (!result && onTouchEvent(event)) {
/*对于松开的事件,*/
case MotionEvent.ACTION_UP:
post(mPerformClick)
其中的post最终会调用到mPerformClick(PerformClick)中的run方法:
private final class PerformClick implements Runnable {
@Override
public void run() {
performClick();
/*如果设置了mOnClickListener */
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
/*调用onClick方法*/
li.mOnClickListener.onClick(this);
所以我们能在打印的堆栈信息中看到:
at com.example.administrator.app_0001_leddemp.MainActivity$MyButtonListener.onClick(MainActivity.java:67)
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22445)
其实中run中被调用的,最终执行我们button中的onClick,详细的调用过程,大家可以根据堆栈信息,继续详细的分析,下面是一个总结的框图: