天天看点

06.输入系统:第10课第24节_输入系统_多点触摸驱动程序_InputStage

在前面的小节中,我们以按键的实例,详细的讲解InputStage,该小节多点触摸屏的InputStage,看看Reader线程是如何处理他的,在这之前先来回顾一下之前的知识。

我们知道一个应用程序APP有多个activity,每个activity对应一个window,在window中存在Decorview,我们可以从其中划出一块区域,然后自由创作,比如在上面增加TextView,Button等等。下面是之前我们学习过的理论流程,如果不记得了,可以看看前面的小节:

06.输入系统:第10课第24节_输入系统_多点触摸驱动程序_InputStage

为了方便的阅读源代码,下面是一个思维导图:

06.输入系统:第10课第24节_输入系统_多点触摸驱动程序_InputStage

从图中的左下角我们可以找到:

06.输入系统:第10课第24节_输入系统_多点触摸驱动程序_InputStage

我们从触摸屏的ViewPostImeInputStage处开始分析,即输入法之后的处理。对于触摸屏,在输入法之前,他没有做任何事情,所以我们不用关心。打开源码文件ViewRootImpl.java。

如,我们可以看到以下代码,在输入法之前:

final class ViewPreImeInputStage extends InputStage {
    protected int onProcess(QueuedInputEvent q) {
       if (q.mEvent instanceof KeyEvent) {
           return processKeyEvent(q);
       }
       return FORWARD;
    }
           

这里我们可以知道,输入法之前,如果是触摸屏事件,直接返回FORWARD,他只处理按键类事件。所以我们只关心输入法之后的处理。假设我们有一个应用程序,其界面如下:

06.输入系统:第10课第24节_输入系统_多点触摸驱动程序_InputStage

如果我们点击界面的button1,他是怎么找到的呢?

1.根据触点的x,y坐标,递归的找到目标控件:先把事件发送给Layout1,看看是否位于其上,如果不是则发送给Layout2,直到其位于Layout3,与Layout3匹配之后在发送给其中的button1,但是其不位于button1,就会发送的button2,这样就找到了这个button。

2.btton如何处理这个事件呢?一般分为两种,click(点击:松开后处理),touch(触摸:按下,松开,滑动都可以处理)。

我们利用取巧的办法,在这两个函数中,添加栈的打印信息。这样我们就能知道其调用过程了。下面是官方文档的一些介绍:

06.输入系统:第10课第24节_输入系统_多点触摸驱动程序_InputStage

从上面可以看到,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 又为一个接口,我们找到其定义:

可知每个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);
    }
           

其处理流程,就与我们之前总结的:

06.输入系统:第10课第24节_输入系统_多点触摸驱动程序_InputStage

的流程是一样的了,Activity传递触摸屏事件给wind不能处理传递给DecorView,最后传递我们的焦点,即按钮上。我上面我们可以看到dispatchTouchEvent函数调用了superDispatchTouchEvent,该实现为:

我们在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为组View的意思,代表一个View可能放有多个控件:

06.输入系统:第10课第24节_输入系统_多点触摸驱动程序_InputStage

在前面的框图中,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,详细的调用过程,大家可以根据堆栈信息,继续详细的分析,下面是一个总结的框图:

06.输入系统:第10课第24节_输入系统_多点触摸驱动程序_InputStage

继续阅读