1. 調用InputMethodManager類的靜態成員函數peekInstance獲得一個類型為InputMethodManager輸入法管理器imm;
2. 調用ViewRoot類的成員函數enqueuePendingEvent將參數event所描述的鍵盤事件緩存起來,等到輸入法處理完成該鍵盤事件之后,再繼續對它進行處理;
3. 調用第1步獲得的輸入法管理器imm的成員函數dispatchKeyEvent來將參數event所描述的鍵盤事件分發給輸入法處理。
這里有兩個地方是需要注意的。第一個地方是只有當前窗口正在顯示輸入法的情況下,ViewRoot類的成員函數deliverKeyEvent才會將參數event所描述的鍵盤事件分發給輸入法處理,這是通過檢查ViewRoot類的成員變量mLastWasImTarget的值是否等于true來確定的。第二個地方是在將參數event所描述的鍵盤事件分發給輸入法處理時,ViewRoot類的成員函數deliverKeyEvent會同時傳遞一個類型為InputMethodCallback的回調接口給輸入法,以便輸入法處理完成參數event所描述的鍵盤事件之后,可以調用這個回調接口的成員函數finishedEvent來向窗口發送一個鍵盤事件處理完成通知。這個類型為InputMethodCallback的回調接口就保存在ViewRoot類的成員變量mInputMethodCallback中,當它的成員函數finishedEvent被調用的時候,它就會調用ViewRoot類的成員函數deliverKeyEventToViewHierarchy來繼續將參數event所描述的鍵盤事件分發給窗口處理。
如果窗口當前不需要與輸入法交互,即ViewRoot類的成員變量mLastWasImTarget的值等于false,那么ViewRoot類的成員函數deliverKeyEvent就會直接調用成員函數deliverKeyEventToViewHierarchy來將參數event所描述的鍵盤事件分發給窗口處理。
接下來,我們就先析窗口在輸入法之前處理鍵盤輸入的過程,接著再分析窗口在輸入法之后處理鍵盤輸入的過程。
從前面的分析可以知道,ViewRoot類的成員函數deliverKeyEvent是通過調用DecorView類的成員函數dispatchKeyEventPreIme來將獲得的鍵盤輸入優先于輸入法分發給窗口處理的。DecorView類的成員函數dispatchKeyEventPreIme是從父類ViewGroup繼承下來的,因此,接下來我們就繼續分析ViewGroup類的成員函數dispatchKeyEventPreIme的實現。
Step 2. ViewGroup.dispatchKeyEventPreIme
[java] view plaincopyprint?
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
......
// The view contained within this ViewGroup that has or contains focus.
private View mFocused;
......
@Override
public boolean dispatchKeyEventPreIme(KeyEvent event) {
if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
return super.dispatchKeyEventPreIme(event);
} else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
return mFocused.dispatchKeyEventPreIme(event);
}
return false;
}
......
}
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
......
// The view contained within this ViewGroup that has or contains focus.
private View mFocused;
......
@Override
public boolean dispatchKeyEventPreIme(KeyEvent event) {
if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
return super.dispatchKeyEventPreIme(event);
} else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
return mFocused.dispatchKeyEventPreIme(event);
}
return false;
}
......
}
這個函數定義在文件frameworks/base/core/java/android/view/ViewGroup.java中。
ViewGroup類的成員函數dispatchKeyEventPreIme首先是檢查當前正在處理的視圖容器是否能夠獲得焦點。如果能夠獲得焦點的話,那么ViewGroup類的成員變量mPrivateFlags的FOCUSED位就會等于1。在當前正在處理的視圖容器能夠獲得焦點的情況下,還要檢查正在處理的視圖容器是否已經計算過大小了,即檢查ViewGroup類的成員變量mPrivateFlags的HAS_BOUNDS位是否等于1。只有在已經計算過大小并且能夠獲得焦點的情況下,那么正在處理的視圖容器才有資格處理參數event所描述的鍵盤事件。注意,正在處理的視圖容器是通過調用其父類View的成員函數dispatchKeyEventPreIme來處理參數event所描述的鍵盤事件的。
如果當前正在處理的視圖容器沒有資格處理參數event所描述的鍵盤事件,但是它有一個能夠獲得焦點的子視圖,并且這個子視圖的大小也是已經計算好了的,那么ViewGroup類的成員函數dispatchKeyEventPreIme就會將參數event所描述的鍵盤事件分發給該子視圖處理。當前正在處理的視圖容器能夠獲得焦點的子視圖是通過ViewGroup類的成員變量mFocused來描述的,通過調用這個成員變量所描述的一個View對象的成員函數dispatchKeyEventPreIme即可將參數event所描述的鍵盤事件分發給夠獲得焦點的子視圖處理。
一個視圖容器是如何知道它的焦點子視圖的呢?我們知道,當我們在屏幕上觸摸一個窗口時,就會發生一個Pointer事件。這個Pointer事件關聯有一個觸摸點,通過檢查這個觸摸點當前是包含在窗口頂層視圖的哪一個子視圖里面,就可以知道哪一個子視圖是焦點子視圖了。
從上面的分析可以知道,無論是當前正在處理的視圖容器獲得焦點,還是它的子視圖獲得焦點,最終都是通過調用View類的成員函數dispatchKeyEventPreIme來在輸入法之前處理參數event所描述的鍵盤事件,因此,接下來我們就繼續分析View類的成員函數dispatchKeyEventPreIme的實現。
原文轉自:http://blog.csdn.net/luoshengyang/article/details/8636153