這幾天在尋找android browser的一個bug時,發現這個函數停經典的,趕緊記錄下來,呵呵。
這裡我們以IME在www.google.com網頁的輸入框(TextDialog view)中輸入“弟弟ff”為例,首先假設已經輸入了“ff”。
為了輸入“弟弟”,使用者在IME的軟鍵盤上敲d字元兩次,然後“dd”會被聯想為“弟弟”,然後使用者在IME的備選框中選擇聯想得到的“弟弟”。 "dd"到“弟弟”這個步驟會導致如下函數被調用:
onTextChanged(CharSequence s,int start,int before,int count)
//參數s為“弟弟ff” before為2,表示有兩個字元(“dd”)被刪掉了 count為 2,表示有兩個字元(“弟弟”)被添加了。
//before和count的含義在TextView(cupcake/frameworks/base/core/java/android/widget/TextView.java)的//onTextChanged函數中有注釋。
下面把這個函數代碼列出來並附加一點注釋
cupcake/frameworks/base/core/java/android/webkit/TextDialog.java:
protected void onTextChanged(CharSequence s,int start,int before,int count){
super.onTextChanged(s, start, before, count);
String postChange = s.toString(); //postChange為“弟弟ff”
// Prevent calls to setText from invoking onTextChanged (since this will
// mean we are on a different textfield). Also prevent the change when
// going from a textfield with a string of text to one with a smaller
// limit on text length from registering the onTextChanged event.
if (mPreChange == null || mPreChange.equals(postChange) ||
(mMaxLength > -1 && mPreChange.length() > mMaxLength &&
mPreChange.substring(0, mMaxLength).equals(postChange))) {
return;
}
mPreChange = postChange; //mPreChange之前的值為"ddff"
// This was simply a delete or a cut, so just delete the
// selection.
//這段代碼不會執行,它用來處理刪除和剪下的。 請注意:在android browser中有一個資料結構render tree來
//表示資料,我們需要更新它,同時還有一個CachedText來表示TextDialog中的資料,兩個都需要更新。
//mWebView.deleteSelection(start, start + before)代碼更新了render tree這種底層資料, 而
//updateCachedTextfield();代碼更新了cached text。
if (before > 0 && 0 == count) {
mWebView.deleteSelection(start, start + before);
// For this and all changes to the text, update our cache
updateCachedTextfield();
return;
}
// Find the last character being replaced. If it can be represented by
// events, we will pass them to native (after replacing the beginning
// of the changed text), so we can see javascript events.
// Otherwise, replace the text being changed (including the last
// character) in the textfield.
//這段注釋很好,“弟弟”中的最後一個字元“弟”不會從kmap中得到KeyEvent,所以(cannotUseKeyEvents=0)
//會直接調用mWebView.replaceTextfieldText()(更新browser下層的表示TextDialog的節點的string)把“dd”
//替換為“dd”,同時updateCachedTextfield()被調用去更新cachedtext.因為“弟”不能被映射為一個KeyEvent,
//所以 sendDomEvent(events[i])不會調用,沒有event被送給webkit中javascript部分。
TextUtils.getChars(s, start + count - 1, start + count, mCharacter, 0);
KeyCharacterMap kmap =
KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
KeyEvent[] events = kmap.getEvents(mCharacter);
boolean cannotUseKeyEvents = null == events;
int charactersFromKeyEvents = cannotUseKeyEvents ? 0 : 1;
if (count > 1 || cannotUseKeyEvents) {
String replace = s.subSequence(start,
start + count - charactersFromKeyEvents).toString();
mWebView.replaceTextfieldText(start, start + before, replace,
start + count - charactersFromKeyEvents,
start + count - charactersFromKeyEvents); //在browser底層“dd”替換為“弟弟”
} else {
// This corrects the selection which may have been affected by the
// trackball or auto-correct.
mWebView.setSelection(start, start + before);
}
updateCachedTextfield(); //把cached text中“dd”替換為“弟弟”
if (cannotUseKeyEvents) {
return;
}
int length = events.length;
for (int i = 0; i < length; i++) {
// We never send modifier keys to native code so don't send them
// here either.
if (!KeyEvent.isModifierKey(events[i].getKeyCode())) {
sendDomEvent(events[i]);
}
}
}