實現一個Android輸入法

原文來自Android Developer Guide,本文爲原文翻譯,如有錯誤,歡迎指出。

輸入法(IME:Input method editor)是一個能夠讓用戶輸入文本的工具。Android提供了一個可擴展的輸入法框架,允許應用爲用戶提供不同的輸入法,比如觸屏鍵盤甚至語音輸入。只要安裝,用戶就可以從系統設置中選擇自己喜歡使用的輸入法,並且在整個系統環境中使用;在同一時刻,只有一種輸入法可以使用。

要在Android系統中創建一個輸入法,你創建的應用需要包含繼承自InputMethodService的類。此外,通常還要創建一個”settings”的activity,用來向IME服務傳遞參數選項。你也可以自定義設置界面,把它作爲系統設置的一部分。

本文包括以下內容:

  • 輸入法的生命週期
  • 在application manifest中聲明IME組件
  • 輸入法 API
  • 設計輸入法的用戶界面
  • 從輸入法嚮應用程序發送文本
  • 創建不同類型的輸入法

如果你之前沒有做過輸入法的相關東西,你應該先閱讀這篇介紹性的文章Onscreen Input Methods。此外,SDK中包含的Soft Keyboard sample app項目裏面有樣例代碼,你可以修改它們,然後開始創建自己的輸入法。

輸入法的生命週期

下圖描述了一個輸入法的生命週期:

Android輸入法生命週期

接下來的部分描述瞭如何實現遵循這一生命週期,實現輸入法代碼和界面。

在application manifest中聲明IME組件

在Android系統中,輸入法是一個包含指定的IME服務的Android應用,應用的mainifest文件必須聲明此服務,請求所需的權限,提供和action.view.InputMethod匹配的intent filter,並且提供包含輸入法一些參數的元數據。此外,若想提供一個允許用戶修改輸入法行爲的設置界面,你要定義一個能從系統設置中啓動的settingsactivity。

下面的代碼聲明瞭一個輸入法的service.它請求了BIND_INPUT_METHOD 權限,來允許服務把IME和系統關聯起來,創建一個intent filter來匹配android.view.InputMethod,並且定義了輸入法的元數據。

<!-- 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>

下一個代碼片段聲明瞭輸入法的設置activity.它包含了一個ACTION_MAINintent filter,表示此activity作爲整個輸入法應用的入口。

<!-- Optional: an activity for controlling the IME settings -->
<activity android:name="FastInputIMESettings" 
    android:label="@string/fast_input_settings">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
    </intent-filter>
</activity>

你也可以在輸入法界面中直接提供用戶設置。

輸入法API

輸入法相關的類可以在android.inputmethodserviceandroid.view.inputmethodpackages中找到。KeyEvent類在處理鍵盤字符中非常重要。

輸入法的核心組件是一個服務,它是一個繼承自InputmethodService的類。除了實現常規的輸入法聲明週期外,該類還包含了一些回調函數,用來給開發者處理輸入法界面,處理用戶輸入,並將文本發送給當前正在處理的文本域。默認情況下,InputmethodService類提供了管理輸入法狀態、可視化以及和當前輸入法框交互的大部分實現。

下面的類也非常重要:

BaseInputConnection

定義了從輸入法回到當前接收輸入的應用之間的通信管道。你可以使用它來獲取焦點所在位置周圍的文本,向文本域提交文本以及嚮應喲功能程序發送原始的鍵盤事件。應用應該繼承這個類而不要獨自實現基礎的InputConnection接口。

KeyboardView

繼承自View,用來渲染繪製一個鍵盤,並且響應用戶的輸入事件。鍵盤的佈局就是用一個Keyboard的實例來實現的,你可以通過創建一個xml文件來實現。

設計輸入法的用戶界面

輸入法中主要有兩種可視化的元素:輸入界面和候選界面。你只需要實現和你設計的輸入法有關的界面。

輸入界面

輸入法界面是用戶輸入文本的地方,可以是敲擊鍵盤鍵位,手寫,或者手勢的形式。當輸入法第一次調用顯示的時候,系統會調用onCreateInputView()。在方法實現中,你可以創建想顯示的佈局,並將創建的佈局實例返回給系統,下面的代碼片段是一個實現onCreateInputView()方法的例子:

@Override 
public View onCreateInputView() { 
    MyKeyboardView inputView = 
        (MyKeyboardView) getLayoutInflater().inflate( R.layout.input, null);

    inputView.setOnKeyboardActionListener(this);                                                  
    inputView.setKeyboard(mLatinKeyboard); 
return mInputView; 
} 

在這個例子中,MyKeyboardView是一個用來生成一個鍵盤的典型的例子.如果你要創建一個傳統的QWERTY鍵盤,可以查看Soft Keyboard sample app作爲一個如何繼承KeyboardView類的樣例。

候選界面

候選界面是輸入法爲用戶提示預測糾錯和建議選詞的地方。在輸入法的生命週期中,當候選界面準備顯示的時候,系統會調用OnCreateCandidateView(),你在實現此方法的時候,要返回用來顯示建議詞的界面,如果不想顯示任何界面,就返回null。(此方法默認返回null,如果不希望顯示就不必實現此方法)

關於實現給用戶提供建議的例子,可以查看Soft Keyboard sample app

界面設計要考慮的事情

這部分介紹一些關於輸入法界面設計的東西。

處理不同屏幕尺寸

你的輸入法界面必須能夠適應不同的屏幕尺寸,並且可以處理橫屏和豎屏。在非全屏輸入模式中,給應用留有足夠的空間來顯示文本框以及相關的內容,所以輸入法不能佔用超過一半的屏幕空間。在全屏輸入的模式中,這就不是問題了。

處理不同的輸入類型

Android中的文本域允許開發者指定一個具體的輸入類型,比如自由格式的文本,數字,URL,郵箱地址和查詢字符串等。當你實現一個新的輸入法的時候,你需要去檢測每一個輸入域的類型,並且爲他提供合適的輸入法實例。當然,你不需要去檢測用戶輸入的數據在特定的輸入類型中的合理性,那是應用的開發者的責任。

例如,這裏有一些Android系統提供的拉丁鍵盤的在處理文本和電話號碼輸入界面的截圖

當一個輸入域得到輸入焦點並且啓動你的輸入法的時候,系統會調用onStartInputView(),並且傳入一個EditorInfo對象參數,裏面包含了關於輸入類型和其他文本域屬性的細節,在這個對象中,inputType字段包含了文本域的輸入類型。

inputType字段是一個整型變量,它包含了基於位的規則表示各種各樣的輸入類型設置。若需要用它來檢測不同的輸入類型,用它和常量TYPE_MASK_CLASS進行如下操作:

**inputType & InputType.TYPE_MASK_CLASS **

輸入類型的位模式可以是下面多種值之一:

  • TYPE_CLASS_NUMBERA 輸入數字的區域,就像前面截圖中展示的那樣,拉丁鍵盤在這種輸入模式下顯示了一個數字面板。

  • TYPE_CLASS_DATETIMEA 用於輸入日期和時間的文本域。

  • TYPE_CLASS_PHONEA 用於輸入電話號碼的文本域

  • TYPE_CLASS_TEXTA 可以輸入所有支持的字符的文本域。

這些常量在參考文檔InputType中,有更加詳細的描述。

輸入類型還包括其他的數值,表示普通文本輸入的變體,例如:

  • TYPE_TEXT_VARIATION_PASSWORDA 用於輸入密碼的TYPE_CLASS_TEXT的變體,輸入法會將輸入的字符顯示爲圓點。

  • TYPE_TEXT_VARIATION_URIA 用於輸入URL和URI的TYPE_CLASS_TEXT變體。

  • TYPE_TEXT_FLAG_AUTO_COMPLETEA 用於輸入可以通過詞典、查詢或者其他組件自動完成的文本。

要用這些常量檢查輸入類型的時候,注意要用正確的常量去和inputType相與,可用的常量列表包含在文檔InputType中。

注意:在你實現的輸入法中,輸入密碼的時候,請確保正確的處理文本,在輸入界面和候選界面中都要隱藏密碼,同時,你不應該在設備中存儲密碼。要了解更多,參考安全性設計指引。

從輸入法嚮應用程序發送文本

當用戶使用你開發的輸入法輸入文本時,你可以發送獨立的鍵盤事件或者編輯光標所在的文本,來將文本發送到應用程序。在任何一種情況中,你可以使用一個InputConncetion實例來發送文本,可以通過調用InputMethodService.getCurrentInputConnection()來獲得實例。

編輯光標所在文本

在一個文本框中處理文本的時候,BaseInputConnection類中包含了一些有用的方法:

getTextBeforeCursor()

返回一個包含光標前指定個數的字符CharSequence

getTextAfterCursor()

返回一個包含光標後面指定個數的字符CharSequence

deleteSurroundingText()

刪除光標前後指定個數的字符

commitText()

提交文本並且重新設置光標的位置。

例如,下面的代碼片段展示瞭如何用文本『Hello』替換文本『Fell』左邊的內容。

InputConnection ic = getCurrentInputConnection();
ic.deleteSurroundingText(4, 0);
ic.commitText("Hello", 1);
ic.commitText("!", 1);

發送前組織文本

如果你的輸入法支持文本預測或者需要多個步驟來組成一個符號或者單詞,你可以在文本編輯區域展示這個過程,直到用戶最終提交了這個單詞,然後你可以用完整的文本替換掉部分的編輯中的文本。你可以在間斷的通過InputConnection#setComposingText()提交文本。

下面的代碼展示瞭如何在文本域中顯示過程:

InputConnection ic = getCurrentInputConnection();
ic.setComposingText("Composi", 1);
...
ic.setComposingText("Composin", 1);
...
ic.commitText("Composing ", 1);

下面 的截圖顯示了用戶看到的界面:

監聽硬鍵盤事件

輸入法窗口沒有明確的焦點,它會首先接收來自硬鍵盤的事件,並且決定是處理事件還是繼續嚮應用傳遞轉發事件。例如,你可能會使用方向鍵在輸入法界面中進行候選詞的選擇操作,也可能使用退格鍵來取消所有的從輸入法窗口生成的彈出界面。

要監聽硬鍵盤事件,覆寫onKeyDown()oKeyUp方法,查看Softkeybaord sample app 中的例子。

記得調用super()方法,如果你不想自己處理這些事件。

創建輸入法子類型

子類型允許輸入法展現不同的輸入模式,同時在一個輸入法中支持不同語言。一個子類型可能是:

  • 一個區域 比如en_US或者fr_FR
  • 一個輸入模式,比如聲音、鍵盤或者手寫
  • 其他輸入法風格,表單或者其他特性,比如九健或者全鍵盤佈局

基本上,模式可以是任何文本描述的,類似『鍵盤』,『聲音』諸如此類。

子類型信息用於輸入法切換的對話框,可以通過通知欄中的輸入法切換或輸入法設置中找到。這些信息可以讓框架爲輸入法直接選擇專門的子類型。當你創建一個輸入法的時候,使用子類型,因爲它可以幫助用戶識別和在不同的輸入法語言和模式中切換。

使用<subtype>標籤,在輸入法的xml資源文件中定義子類型。下面的代碼片段定義了一個包含兩種子類型的輸入法:一個針對美國英語區域,另一個針對法法語地區:

<input-method xmlns:android="http://schemas.android.com/apk/res/android"
        android:settingsActivity="com.example.softkeyboard.Settings"
        android:icon="@drawable/ime_icon"
    <subtype android:name="@string/display_name_english_keyboard_ime"
            android:icon="@drawable/subtype_icon_english_keyboard_ime"
            android:imeSubtypeLanguage="en_US"
            android:imeSubtypeMode="keyboard"
            android:imeSubtypeExtraValue="somePrivateOption=true"
    />
    <subtype android:name="@string/display_name_french_keyboard_ime"
            android:icon="@drawable/subtype_icon_french_keyboard_ime"
            android:imeSubtypeLanguage="fr_FR"
            android:imeSubtypeMode="keyboard"
            android:imeSubtypeExtraValue="foobar=30,someInternalOption=false"
    />
    <subtype android:name="@string/display_name_german_keyboard_ime"
            ...
    />
/>

爲了確保你的子類型在用戶界面中標記正確。使用%s類獲取和子類型區域一樣的標籤,下面的兩段代碼演示了這個方法。第一段代碼來自輸入法xml配置文件的一部分:

<subtype
    android:label="@string/label_subtype_generic"
    android:imeSubtypeLocale="en_US"
    android:icon="@drawable/icon_en_us"
    android:imeSubtypeMode="keyboard" />

下一段代碼是strings.xml文件的一部分,label_subtype_generic字符串資源用來設置輸入法UI定義子類型的標籤:

<string name="label_subtype_generic">%s</string> 在任何的英語區域,這段代碼會將子類型顯示的名字設置爲『英語(美國)』,或者對應其他區域的語言。

通過通知欄選擇輸入法的子類型

Android系統會管理所有輸入法定義的子類型。輸入法子類型作爲定義他們的輸入法的一種模式存在。在通知欄,用戶可以選擇當前使用的輸入法的可用子類型,如下面的截圖所示:

通過系統設置選擇輸入法子類型

在系統設置的語言與輸入設置面板中,用戶可以選擇子類型如何被使用。在Soft Keyboard的例子中,InputMethodSettingsFragment.java文件實現了一個輸入法設置中的子類型激活開關。請參照Android SDK中輸入法的例子,獲得更多關於輸入法子類型的信息。

輸入法注意事項

在開發你自己的輸入法時,還有一些要注意的問題:

  • 讓用戶可以在輸入法界面中直接進入設置功能
  • 因爲設備上可能安裝了不同類型的輸入法,讓用戶可以在輸入法界面上直接切換不同的輸入法
  • 快速的渲染輸入法界面,預加載或者只加載需要的大的資源,這樣用戶可以在敲擊文本框的時候立刻看到輸入法界面。緩存資源和視圖以便之後對輸入法的調用。
  • 相反,你應該在輸入法界面隱藏後,釋放佔用較大的內存,使應用程序擁有充足的內存運行。當輸入法在一小段時間處於隱藏狀態時,可以考慮使用延時消息來釋放資源。
  • 確保用戶在所處的語言區域中輸入儘可能多的字符。記住用戶可能在用戶名或者密碼中使用標點符號,所以你的輸入法需要提供儘可能多的不同的字符,以便用戶可以輸入密碼來訪問設備。

轉自: http://atleeon.com/code/2013/10/24/implementation-of-android-inputmethod/

發佈了340 篇原創文章 · 獲贊 4 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章