二. TextView控件獲取鍵盤輸入的過程分析
從前面Android應用程序鍵盤(Keyboard)消息處理機制分析一文可以知道,每一個窗口的創建的時候,都會與系統的輸入管理器建立一個用戶輸入接收通道。輸入管理器在啟動兩個線程,其中一個用來監控用戶輸入,即監控用戶是否按下或者放開了鍵盤按鍵,或者是否觸摸了屏幕,另外一個用來將監控到的用戶輸入事件分發給當前激活的窗口來處理,而這個分發過程就是通過前面建立的通道來進行的。
當前激活的窗口接收到輸入管理器分發過來的用戶輸入事件之后,就會該事件封裝成一個消息發送到當前激活的窗口所運行在的應用程序進程的主線程的消息隊列中去。等到這個消息被處理的時候,就會調用與當前激活的窗口所關聯的一個ViewRoot對象的成員函數deliverKeyEvent或者deliverPointerEvent來將前面接收到的用戶輸入分發給合適的控件。其中,ViewRoot類的成員函數deliverKeyEvent負責分發鍵盤輸入事件,而ViewRoot類的成員函數deliverPointerEvent負責分發觸摸屏輸入事件。
接下來,我們就從ViewRoot類的成員函數deliverKeyEvent開始,分析一個TextView控件獲得鍵盤輸入的過程(獲得觸摸屏輸入的過程是類似的),如圖2所示:
圖2 TextView控件獲得鍵盤輸入的過程
這個過程可以分為14個步驟,接下來我們就詳細分析每一個步驟。
Step 1. ViewRoot.deliverKeyEvent
[java] view plaincopyprint?
public final class ViewRoot extends Handler implements ViewParent,
View.AttachInfo.Callbacks {
......
private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
// If mView is null, we just consume the key event because it doesn't
// make sense to do anything else with it.
boolean handled = mView != null
? mView.dispatchKeyEventPreIme(event) : true;
if (handled) {
if (sendDone) {
finishInputEvent();
}
return;
}
// If it is possible for this window to interact with the input
// method window, then we want to first dispatch our key events
// to the input method.
if (mLastWasImTarget) {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null && mView != null) {
int seq = enqueuePendingEvent(event, sendDone);
......
imm.dispatchKeyEvent(mView.getContext(), seq, event,
mInputMethodCallback);
return;
}
}
deliverKeyEventToViewHierarchy(event, sendDone);
}
......
}
public final class ViewRoot extends Handler implements ViewParent,
View.AttachInfo.Callbacks {
......
private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
// If mView is null, we just consume the key event because it doesn't
// make sense to do anything else with it.
boolean handled = mView != null
? mView.dispatchKeyEventPreIme(event) : true;
if (handled) {
if (sendDone) {
finishInputEvent();
}
return;
}
// If it is possible for this window to interact with the input
// method window, then we want to first dispatch our key events
// to the input method.
if (mLastWasImTarget) {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null && mView != null) {
int seq = enqueuePendingEvent(event, sendDone);
......
imm.dispatchKeyEvent(mView.getContext(), seq, event,
mInputMethodCallback);
return;
}
}
deliverKeyEventToViewHierarchy(event, sendDone);
}
......
}
這個函數定義在文件frameworks/base/core/java/android/view/ViewRoot.java中。
參數event描述的是窗口接收到的鍵盤事件,另外一個參數sendDone表示該鍵盤事件處理完成后,是否需要向系統的輸入管理器發送一個通知。
ViewRoot類的成員變量mView描述的是窗口的頂層視圖,即它指向的是一個DecorView對象,ViewRoot類的成員函數deliverKeyEvent首先是調用它的成員函數dispatchKeyEventPreIme來讓它優先于輸入法處理參數event所描述的鍵盤事件。如果這個DecorView對象的成員函數dispatchKeyEventPreIme的返回值handled等于true,那么就說明參數event所描述的鍵盤事件已經處理完畢,即ViewRoot類的成員函數deliverKeyEvent不用往下執行了。在這種情況下,如果參數sendDone的值等于true,那么ViewRoot類的成員函數deliverKeyEvent在返回之前,還會調用成員函數finishInputEvent來通知系統的輸入管理器,當前激活的窗口已經處理完成剛剛發生的鍵盤事件了。在接下來的Step 2到Step 4中,我們再詳細分析鍵盤事件優先于輸入法分發給窗口處理的過程。
假設窗口不在輸入法前面攔截參數event所描述的鍵盤事件,接下來ViewRoot類的成員函數deliverKeyEvent就會將該鍵盤事件分發給輸入法處理,這個分發過程如下所示:
原文轉自:http://blog.csdn.net/luoshengyang/article/details/8636153