在Android中建立一種新的IME(Creating an Input Method))一.
1.建立一個新的IME需要繼承android.inputmethodservice.InputMethodService,這個類提供了一個IME
的基本實現,例子可以參考sdk中的SoftKeyboard的代碼。
2.IME跟其他application或service一樣會被打包成一個apk,在 AndroidManifest.xml,把它聲明成一個
service.[code]<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.fastinput">
<application android:label="@string/app_label">
<!-- Declares the input method service -->
<service android:name="FastInputIME"
android:label="@string/fast_input_label"
android:permission="android.permission.BIND_INPUT_METHOD">
<intent-filter>
<action android:name="android.view.InputMethod" />
</intent-filter>
<meta-data android:name="android.view.im" android:resource="@xml/method" />
</service>
<!-- Optional activities. A good idea to have some user settings. -->
<activity android:name="FastInputIMESettings" android:label="@string/fast_input_settings">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
</application>
</manifest>[/code]3.IME的service生命週期如下
[img]https://docs.google.com/a/google.com/File?id=ad9xdxhtb4_043qn7phd_b[/img]
二.IME介面元素
IME有2個主要的介面元素,InputView與Candidates View。
InputView:是使用者輸入文字的地方,當IME被顯示的時候會調用InputMethodService.onCreateInputView()
,在這個函數裡建立和返回你想在IME視窗中顯示的Input View.
Candidates View:是用來提供輸入選擇,在函數InputMethodService.onCreateCandidatesView()中建立,預設
為空白。
三.設計不同的輸入類型
一個程式的文字框可能有不同的輸入類型,比如字元,數字,url,email地址等,當你實現一種IME的時候你
就需要知道不同輸入方式的區別,IME不會自動根據不同的輸入類型來切換,所以你的IME需要支援所有的
輸入類型。至於輸入資料的驗證就交由應用程式去負責。
例如,Android中一個LatinIME提供的字元與數字輸入的介面:
[img]https://docs.google.com/a/google.com/File?id=ddvqp2ns_18g2nm5jhb_b[/img]
[img]https://docs.google.com/a/google.com/File?id=ddvqp2ns_17crk39fd9_b[/img]
調用InputMethodService.onStartInputView()的時候會傳遞一個 EditorInfo對象來判斷輸入類型。
例如使用(EditorInfo.inputType & EditorInfo.TYPE_CLASS_MASK)來判斷是屬於下面的哪種類型:
TYPE_CLASS_NUMBER
TYPE_CLASS_DATETIME
TYPE_CLASS_PHONE
TYPE_CLASS_TEXT
密碼輸入:注意不要在你的介面中顯示密碼,除了提醒使用者外也不要把密碼儲存起來。
四.把輸入文本傳送給應用程式
1.可以發送一個key event來實現[code]InputConnection ic = getCurrentInputConnection();
long eventTime = SystemClock.uptimeMillis();
ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 0, 0,
KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
KeyEvent.ACTION_UP, keyEventCode, 0, 0, 0, 0,
KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));[/code]或者 [code]InputMethodService.sendDownUpKeyEvents(keyEventCode);[/code]建議對於一些 輸入模式使用第一種方法,因為有些按鍵可能被過濾。
2.通過編輯輸入文本,主要使用以下方法。[code]getTextBeforeCursor()
getTextAfterCursor()
deleteSurroundingText()
commitText()[/code]比如,一個以Fell開頭的文本,你想把它替換成Hello![code]InputConnection ic = getCurrentInputConnection();
ic.deleteSurroundingText(4, 0);
ic.commitText("Hello", 1);
ic.commitText("!", 1);[/code]五.Word Flow
如果你需要Word Flow或者輸入過程中動態預測輸入的文本,你可以參考如下代碼:[code]InputConnection ic = getCurrentInputConnection();
ic.setComposingText("Composi", 1);
...
ic.setComposingText("Composin", 1);
...
ic.commitText("Composing ", 1);[/code][img]https://docs.google.com/a/google.com/File?id=ddvqp2ns_0g2z84dg2_b[/img]
[img]https://docs.google.com/a/google.com/File?id=ddvqp2ns_1jnw696d3_b[/img]
[img]https://docs.google.com/a/google.com/File?id=ddvqp2ns_2crrwtbf7_b[/img]
六.攔截硬體按鍵訊息
儘管IME視窗沒有foucs,但是它最先收到硬體的按鍵訊息,如果需要處理這些硬體按鍵訊息,你只需要
重寫InputMethodService.onKeyDown() 與InputMethodService.onKeyUp(),如果你不想處理某個按鍵,記得調
用super.onKey* 。
七.其他注意點
1.提供一個使用者可以直接從當前IME進行相關IME設定的方式。
2.提供一個使用者可以切換不同IME的方式。
3.讓IME介面儘快的彈出,資源或者耗時間長度的操作可以稍後載入。
4.當IME視窗被隱藏的時候,大塊的記憶體配置最好儘快釋放
5.確保IME能包含最常用的字元。
八.例子:
可以參考LatinIME的例子:
[url=http://android.git.kernel.org/?p=platform/packages/inputmethods/LatinIME.git;a=tree]http://android.git.kernel.org/?p=platform/packages/inputmethods/LatinIME.git;a=tree[/url]
或者1.5 SDK也提供了一個SoftKeyboard的例子。
Terry 發表於 2009-4-25 03:21
這是1.5版核心的輸入嗎?挺不錯的
fly_fire 發表於 2009-4-25 09:37
[quote]這是1.5版核心的輸入嗎?挺不錯的
[size=2][color=#999999]Terry 發表於 2009-4-25 03:21[/color] [url=http://yddev.com/bbs/redirect.php?goto=findpost&pid=96&ptid=63][img]http://yddev.com/bbs/images/common/back.gif[/img][/url][/size][/quote]
對,這是基於1.5的IME架構建立新的IME。。
yantao821015 發表於 2009-7-22 16:54
如何通過API去設定當前IME呢?
我看了下setting的代碼,他是用
Settings.Secure.putString(getContentResolver(),
Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
但是,在SDK文檔中明確指出,該設定只能讀,不能寫。鬱悶了。。。
高人給指點一下
http://www.yddev.com/bbs/viewthread.php?tid=63
softkeyboard
總結:
SoftKeyboard.java
主要變數:
StringBuilder mComposing 用於存放向連結應用提交的字串
主要介面函數:
public void onKey(int primaryCode, int[] keyCodes) {
if (isWordSeparator(primaryCode)) { //判斷是否為指定符號(空格,分號,冒號等)。指定符號在string.xml中
// Handle separator
if (mComposing.length() > 0) { //輸入該符號之前是否有輸入
commitTyped(getCurrentInputConnection());//將之前打的文本提交給連線應用程式,並清除輸入狀態
}
sendKey(primaryCode);//執行該按鍵的自身操作(輸入空格、冒號等)
updateShiftKeyState(getCurrentInputEditorInfo());
} else if (primaryCode == Keyboard.KEYCODE_DELETE) {
handleBackspace();
} else if (primaryCode == Keyboard.KEYCODE_SHIFT) {
handleShift();
} else if (primaryCode == Keyboard.KEYCODE_CANCEL) {
handleClose();//關閉軟鍵盤
return;
} else if (primaryCode == LatinKeyboardView.KEYCODE_OPTIONS) {
// Show a menu or somethin'
} else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE
&& mInputView != null) {//切換輸入髮狀態
Keyboard current = mInputView.getKeyboard();
if (current == mSymbolsKeyboard || current == mSymbolsShiftedKeyboard) {
current = mPinyinKeyboard;//mQwertyKeyboard;
} else if(current == mPinyinKeyboard){
current = mQwertyKeyboard;//
} else {
current = mSymbolsKeyboard;
}
mInputView.setKeyboard(current);
if (current == mSymbolsKeyboard) {
current.setShifted(false);
}
} else {
handleCharacter(primaryCode, keyCodes);//輸入一般字元或符號
}
}
private void handleCharacter(int primaryCode, int[] keyCodes) {
if (isInputViewShown()) {//軟鍵盤是否開啟
if (mInputView.isShifted()) {//檢查鍵盤狀態
primaryCode = Character.toUpperCase(primaryCode);//轉換大小寫
}
}
if (isAlphabet(primaryCode) && mPredictionOn) {//用charactor.isletter()判斷是否為字元,並且之前有輸入
mComposing.append((char) primaryCode);//向結尾處追加字元
getCurrentInputConnection().setComposingText(mComposing, mComposing.length());//設定當前輸入文本顯示
updateShiftKeyState(getCurrentInputEditorInfo());
updateCandidates();
} else {
getCurrentInputConnection().commitText(
String.valueOf((char) primaryCode), 1);//直接將當前輸入的字元提交給連結的應用
}
}