通过前面的学习,我们知道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,这些称为兜底工作(处理最终无人处理的按键)。