06.输入系统:第10课第17节_输入系统_InputStage_理论

通过前面的学习,我们知道andriod对输入事件的处理分为好几个阶段,再次粘贴一下之前的框图:
在这里插入图片描述
根据之前的中,可以分为输入法之前的处理,输入法处理,输入法之后的处理,以及最后的综合处理。对于java编写的程序,我们只关注View-onKeyDown()的处理。在上小节讲解了activity,window,decor,view之间的关系。

我们知道一个appliction有多个activity,每个activity有多个window,我们需要选定样式decor,然后在指定区域放置各种View。

一个应用程序运行的是当前的某个activity,当他接收到输入事件之后,他会把事件传送给Window处理,Window能理会传送给decor直到view,下面是理论的流程:
1.收到到输入事件之后,发送给当前活动的activity,activity接收到事件之后不能发送给Window处理,如果Window不能处理,activity就会进行兜底(处理)。wind能处理就发送给decor处理。
2.DecorView(decor)接收到以后,如果能处理,就发送给view,如果不能处理,DecorView就会进行兜底(处理)
3.view接收到之后,直接把事件发送给输入焦点。
当然在android系统中并非完全按照这个工作线,先粘贴一下框图,然后围绕这个框图进行讲解:
在这里插入图片描述

输入法之前处理

我们现在阅读以下源代码,根据前面的章节,可以知道InputStage是在ViewRootImpl.java中实现的,打开这个文件,可以看到:

        @Override
        protected int onProcess(QueuedInputEvent q) {//判断是否为按键事件
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            }
            return FORWARD;
        }

        private int processKeyEvent(QueuedInputEvent q) {
            final KeyEvent event = (KeyEvent)q.mEvent;
            if (mView.dispatchKeyEventPreIme(event)) {//mView为DecorView,在输入法之前做的分发
                return FINISH_HANDLED;
            }
            return FORWARD;
        }

该函数流程简单,先判断是否为按键事件,如果为按键事件调用则调用processKeyEvent(q)方法处理这个事件,其mView为DecorView,在这里,然后根据之前的分析,可以得到以下框图:
在这里插入图片描述
我看看dispatchKeyEventPreIme()函数在哪个地方被是实现,打开PhoneWindow.java,我们可以找到DecorView,然后进入他的构造函数,在其文件搜索ispatchKeyEventPreIme,但是并没有搜索到,那么我们看他的父类FrameLayout,在FrameLayout.java也不能搜索到dispatchKeyEventPreIme方法,那么继续查找FrameLayout的父类ViewGroup,在ViewGroup.java中,可以找到dispatchKeyEventPreIme的定义:

public boolean dispatchKeyEventPreIme(KeyEvent event) {
	return mFocused.dispatchKeyEventPreIme(event);

可以看到,这个输入事件,最终传递给mFocused(该为一个输入焦点),调用其dispatchKeyEventPreIme。假如我们的焦点为EditText(文本编辑框),我们看看。该类是否实现了dispatchKeyEventPreIme。在EditText.java中搜索dispatchKeyEventPreIme,没有找到该方法,那么我们查找其父类TextView,在TextView.java中进行搜索,也没有。直到View.java中,我们可以找到该方法的实现:

    public boolean dispatchKeyEventPreIme(KeyEvent event) {
        return onKeyPreIme(event.getKeyCode(), event);//输入法之前对按键的处理
        	return false;
    }

可以看到起调用了onKeyPreIme方法,执行了在输入法之前事件的处理。但是该函数直接返回false。这里我们可以知道,如果想要输入事件在输入法之前做一些处理,那么久需要重写onKeyPreIme方法。

从上面可以看出,在输入法之前的处理ViewPreImeIputStege最终会调用到输入焦点的onKeyPreIme方法。

回顾一下之前的知识点:
在这里插入图片描述
上图是方法的调用过程,从上往下,或者说从右到左。上面的分析是都输入法之前的分析,现在我们来看看输入法之后的处理

输入法之后处理

从新回到ViewRootImpl.java,他是所有阶段处理的入口,我们找到:

final class ViewPostImeInputStage extends InputStage {
	protected int onProcess(QueuedInputEvent q) {
		processKeyEvent(q);
			if (mView.dispatchKeyEvent(event)) {

这样就会对按键事件进行处理了,在PhoneWindow.java中,我们看到一个cb = getCallback()的方法,获得一个一个回调函数,那么肯定存在setCallback,其实现是在每个activity,打开acyivity.java文件,我们在其中也可以看到dispatchKeyEvent的实现。

下面我们根据:
在这里插入图片描述
进行分析,在acyivity.java文件中,找到dispatchKeyEvent:

public boolean dispatchKeyEvent(KeyEvent event) {
	Window win = getWindow();
	   /*如果win不能处理就返回,能处理调用event.dispatch*/
	   if (win.superDispatchKeyEvent(event)) {
	       return true;
	   }
	   View decor = mDecor; 
	   if (decor == null) decor = win.getDecorView();
	   return event.dispatch(this, decor != null? decor.getKeyDispatcherState() : null, this);

从上面我们可以看出,首先acyivity会交给Window ,让其判断能否处理,能处再交给View decor,可以看到其上调用了win.superDispatchKeyEvent(event),我们看看在窗口PhoneWindow.java(电话窗口),可以找到superDispatchKeyEvent方法,该方法:

    public boolean superDispatchKeyEvent(KeyEvent event) {
        return mDecor.superDispatchKeyEvent(event);
    }

可以看到存在两个superDispatchKeyEvent,第一个是在Window.java中实现的,第二个是在DecorView中实现的,这样就从Window进入到DecorView了,在DecorView.java中,我们可以找到superDispatchKeyEvent方法的实现:

public boolean superDispatchKeyEvent(KeyEvent event) {
	return super.dispatchKeyEvent(event);

可以看到,其又调用了父类的dispatchKeyEvent,其父类为FrameLayout,进入FrameLayout.java,没有找到dispatchKeyEvent,那么继续查找去父类ViewGroup.java,可以找到其实现过程:

public boolean dispatchKeyEvent(KeyEvent event) {
	if (mFocused.dispatchKeyEvent(event)) {//寻找到其焦点,调用其dispatchKeyEvent方法

这个焦点我们依旧假设其为一个TextView,通过其父类的查找,直到View.java中,我们可以找到该方法的实现:

public void setOnKeyListener(OnKeyListener l) {//设置监听事件
    getListenerInfo().mOnKeyListener = l;
}

public boolean dispatchKeyEvent(KeyEvent event) {
	ListenerInfo li = mListenerInfo;//获得监听者的信息
	/*如果监听者有mOnKeyListener,者调用其mOnKeyListener.onKey方法*/
    if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
    if (event.dispatch(this, mAttachInfo != null? mAttachInfo.mKeyDispatchState : null, this)) {
       return true;
   }

其上的if (event.dispatch(this, mAttachInfo != null? mAttachInfo.mKeyDispatchState : null, this))与之前我们分析的acyivity.java文件中dispatchKeyEvent方法调用的:

/*View.java*/
event.dispatch(this, mAttachInfo != null? mAttachInfo.mKeyDispatchState : null, this)) 
/*acyivity.java*/
event.dispatch(this, decor != null? decor.getKeyDispatcherState() : null, this);

十分类似,我们在KeyEvent.java找到event.dispatch方法的实现过程:

public final boolean dispatch(Callback receiver, DispatcherState state,Object target) {
	/*下面两个函数为最终的处理*/
	boolean res = receiver.onKeyDown(mKeyCode, this);
	return receiver.onKeyUp(mKeyCode, this);

现在我们总结一下输入法之后的处理:
1.ViewPreImeIputStege最终会调用到输入焦点的3个方
a. setOnKeyListener(使用该方法设置KeyListener注册的监听器的OnKey)
b. onKeyDown
c. onKeyUp
2.或者ViewPreImeIputStege会调用acyivity的onKeyDown与onKeyUp,这些称为兜底工作(处理最终无人处理的按键)。

猜你喜欢

转载自blog.csdn.net/weixin_43013761/article/details/88553162