編輯:Android編程入門
輸入法是一種可以讓用戶輸入文字的控件。Android提供了一套可擴展的輸入法框架,使得應用程序可以讓用戶選擇各種類型的輸入法,比如基於觸屏的鍵盤輸入或者基於語音。當安裝了特定輸入法之後,用戶即可在系統設置中選擇個輸入法,並在接下來的輸入場景中使用該輸入法。不過在任一時刻,只能使用一個輸入法。
為了在安卓系統下創建一個輸入法,需要新建一個包含擴展了InputMethodService類的安卓應用,並創建一個用於設置的activity,用戶可以通過它將設置選項傳給輸入法的service,因此,你還需要為該設置應用定義展現、交互界面,用於顯示和改變輸入法設置。
本指南包含如下內容:
如果你過去沒有接觸過輸入法,建議先讀這篇介紹性文章《OnscreenInputMethods》。在SDK中有一個輸入法例程SoftKeyboard可供參考。
下圖描述了輸入法完整的生命周期:
圖1:輸入法的生命周期
接下來的章節將描述如何實現輸入法在生命周期中每一個節點的編碼。
在安卓系統中,輸入法是一個包含IME service的安卓應用程序。必須在該應用程序的manifest文件中聲明service,請求必要的權限,提供能夠匹配action.view.InputMethod 的intent filter,提供定義輸入法特征的metadata。此外,還要提供一個可以用來修改輸入法參數的設置界面,通過系統設置可以啟動該界面。
如下代碼片段定義了一個輸入法service:
<!-- 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>
第一行粗體字聲明需要BIND_INPUT_METHOD權限來對接系統,第二行粗體字創建了一個能夠匹配android.view.InputMethod的intent filter,第三行粗體字定義了輸入法的metadata。
接下來的代碼片段聲明了輸入法的設置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>
其中的粗體字定義了一個能夠匹配ACTION_MAIN的intent filter,這表明該acitvity是輸入法應用的主入口。
還可以在這裡聲明從UI訪問輸入法設置的權限。
在android.inputmethodservice和android.view.inputmethod包中可以找到輸入法相關的class。其中KeyEvent 是處理字符按鍵的重要類。
輸入法的中心環節就是一個service組件,該組件擴展了InputMethodService。除了實現普通的service生命周期以外,該類需要給UI層提供回調函數,用來處理用戶輸入,並且把文本傳遞給輸入焦點。InputMethodService類實現了大部分管理輸入法狀態、界面以及和當前輸入框通信的邏輯。
以下class同樣重要:
BaseInputConnection
定義了從輸入法到接收輸入的應用程序之間的通信通道。使用該類可以獲取光標附近的文本,可以把字符串提交給文本框,還可以向應用程序發送原生的按鍵消息。應用程序應該擴展該類,而不要實現InputConnection。
KeyboardView
該類擴展了View使其能夠展現出一個鍵盤並且相應用戶的輸入事件。可以通過一個XML文件來定義鍵盤布局。
輸入法有兩個主要的可見的界面元素:輸入窗和候選窗。你只需要實現和輸入法相關的界面元素即可。
Input view
輸入窗是指用戶通過按鍵或手寫或手勢直接產生的文本展示區域。當輸入法首次展現時,系統調用onCreateInputView()回調函數。你需要在該方法中創建輸入法界面布局,並將該布局返回給系統。下面的代碼片段實現了onCreateInputView()方法:
@Override public View onCreateInputView() { MyKeyboardView inputView = (MyKeyboardView) getLayoutInflater().inflate( R.layout.input, null); inputView.setOnKeyboardActionListener(this); inputView.setKeyboard(mLatinKeyboard); return mInputView; }
在該實例中,MyKeyboardView實現了類KeyboardView,用來自定義一個鍵盤。如果你使用傳統的QWERTY鍵盤,請參見KeyboardView類。
Candidates view
候選窗用來展現輸入法轉換過的供用戶選擇的候選字串,系統將調用onCreateCandidatesView()使輸入法創建並展現出候選窗。你需要實現該方法,返回一套布局來展現候選窗,當不需要展現候選窗時可以返回null。該方法默認就會返回null,因此如果你什麼都不做就會什麼都不展現。
在SoftKeyboard例程中你可以找到候選窗實現的例子。
本章講述輸入法中一些特殊的UI設計。
輸入法的UI必須能夠處理不同的屏幕尺寸,需要考慮到屏幕的縱深視圖。在非全屏模式下,輸入法必須為應用程序的輸入框和相關上下文留出足夠的空間,因此輸入法不能占用超過一半的屏幕空間。全屏模式下則不存在這些問題。
安卓的輸入框允許你給他設定輸入類型,比如文本、數字、URL、email地址或者搜索串。當你實現了一個新的輸入法,你需要探測每一個輸入框的輸入類型,並為之提供匹配的界面。當然,你不需要檢查用戶輸入文字的合法性,這是應用程序的職責。
例如,下面是輸入法為輸入類型為文本和電話號碼的輸入框展現的界面截圖:
圖2
當某個輸入控件接收到焦點,輸入法將被啟動,系統會調用輸入法的onStartInputView(),並傳進來一個EditorInfo對象,該對象包含輸入類型和其他輸入控件的相關屬性,其中inputType字段用來表示當前輸入控件的輸入類型。
inputType字段是一個整形數據,它是不同的輸入類型按位或出來的結果。可以使用掩碼TYPE_MASK_CLASS來檢測該字段的值,如:
inputType & InputType.TYPE_MASK_CLASS
其結果可能包含如下值:
TYPE_CLASS_DATETIME 當前輸入控件只接受日期和時間。
TYPE_CLASS_PHONE 當前輸入控件只接受電話號碼。
TYPE_CLASS_TEXT 當前輸入控件接受所有字符。
在InputType的參考手冊文檔中可以找到這些常量的詳細描述。inputType字段還可以包含其他的文本變種類型,例如:
TYPE_TEXT_VARIATION_URI 表明當前文本框是用於輸入URL或者URI字串。
TYPE_TEXT_FLAG_AUTO_COMPLETE 表明在當前文本框中輸入文字時,應用程序會使用字典或搜索引擎或其他機制為其內容自動補全。
在測試這些變種的時候要對inputType使用准確地常亮作比較。在InputType的參考手冊文檔中可以找到所有掩碼常量的詳細信息。
注意:在你的輸入法中,當要把字符傳遞給密碼框時,一定要處理正確:在你的輸入窗和候選窗中務必不要顯示密碼串,輸入法也不要在設備中保存用戶密碼。在《安全設計指南》中可以了解到更多安全議題。
當用戶使用輸入法輸入字符時,輸入法有兩種手段可以將文本發送給應用程序:一、向應用程序發送獨立的鍵盤事件;二、編輯輸入框中光標附近的文本。兩種方式都需要使用一個InputConnection實例來傳遞字符串,調用InputMethodService.getCurrentInputConnection()就可以獲得該實例。
當你對輸入窗中已有的字符串展開編輯時,BaseInputConnection下的一些方法非常有用:
getTextAfterCursor() 返回一個CharSequence對象,該對象包含光標後指定個數的字符。
deleteSurroundingText() 刪除光標前後指定個數的字符。
commitText() 把一個CharSequence對象提交給輸入窗,並設置新的光標位置。
下面的片段顯示了怎樣用“Hello!”替換光標左側的四個字符:
InputConnection ic = getCurrentInputConnection(); ic.deleteSurroundingText(4, 0); ic.commitText("Hello", 1); ic.commitText("!", 1);
如果你的輸入法需要做預測或者要通過幾步組織成象形文字,你可以先在輸入框中顯示當前的輸入過程,最後再把組織成的最終字串提交給輸入框,用這個最終字串替換掉之前的過程串。你可以把中間過程串傳遞給setComposingText()函數來展現這個過程。
下面的代碼段用以說明如何展現這個過程:
InputConnection ic = getCurrentInputConnection(); ic.setComposingText("Composi", 1); ... ic.setComposingText("Composin", 1); ... ic.commitText("Composing ", 1);
以上代碼的執行效果展現如下:
圖3:上屏前的寫作串
盡管輸入法窗口沒有輸入焦點,但它能夠第一個獲得硬鍵盤按鍵消息,並且選擇是否吃掉它還是繼續向下傳遞給應用程序。例如,當方向鍵按下時,你可以在輸入法候選窗上移動焦點候選,並吃掉這個按鍵消息;當退格鍵按下時,你可以取消輸入法窗口彈出的任何輸入窗或候選窗。
覆蓋onKeyDown()和onKeyUp()方法可以攔截硬鍵盤事件。詳情可以參考SoftKeyboard例程。
如果你不想處理該按鍵消息,記得調用父類的super()方法。
輸入法可以通過subtype來定義它所支持的多種輸入模式和語言。一個subtype可以包含如下屬性:
輸入模式可以是任何的鍵盤布局、語音輸入等等形式。一個subtype可以是這些形式的組合。
輸入法可以在自己的選擇面板中讀取subtype信息來切換不同的subtype,通常在系統通知欄和輸入法設置界面中展現該信息。系統框架還可以通過該信息直接創建出一個指定的輸入法subtype。當你構建一個輸入法時,應使用subtype功能,因為他可以幫助用戶區分和切換不同的輸入法語言和模式。
可以再輸入法的XML資源文件中定義subtype,使用<subtype>根元素。下面的片段定義了一款帶有兩個subtype的輸入法:一個是英文鍵盤,另一個是法文鍵盤。
<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" ... /> />
為了保證你的subtype在UI中能正確地標示出來,要使用%s來獲取subtype標簽,這和獲取subtype locale的方法一樣。接下來會用兩個代碼段來示范,其中第一段是輸入法的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>
該設置可以使輸入法的subtype的名字按照本地locale設置來顯示。例如在英文locale下顯示”English (United States)”
所有輸入法暴露出的subtype會被安卓系統統一管理。一款輸入法的所有subtypes均隸屬於該輸入法。如下所示,用戶可以在系統通知欄中,選擇當前輸入法下任一可用的subtype:
圖4:從通知欄中選擇輸入法subtype
圖5:在系統設置面板中設置輸入法的subtype
用戶可以在系統的“語言和輸入”設置面板中設置如何使用subtype。在SoftKeybaord例程的文件InputMethodSettingsFragment.java中包含了如何使用subtype的實現代碼,研究該例程可以了解如何在輸入法中支持subtype的更多信息。
圖6:選擇一個輸入法的語言
可以提供一些切換關鍵字,讓用戶更容易地在多個輸入法subtype之間切換,這些關鍵字也可以是全局的語言圖標。這樣可以極大提升鍵盤的可用性,解決用戶通電。要想能夠方便的切換,需要完成如下步驟:
1、在輸入法的XML資源文件中聲明supportsSwitchingToNextInputMethos=“true”,如下:
<input-method xmlns:android="http://schemas.android.com/apk/res/android" android:settingsActivity="com.example.softkeyboard.Settings" android:icon="@drawable/ime_icon" android:supportsSwitchingToNextInputMethod=“true">
2、調用shouldOfferSwitchingToNextInputMethod()方法。
3、如果該方法返回true,則顯示切換關鍵字。
4、當用戶選擇了切換關鍵字後,調用switchToNextInputMeshod()方法,並在第二個參數中傳入false。該false告訴系統平等對待所有subtype,不管他們屬於哪個輸入法。如果指定true,則要求系統在當前輸入法內循環切換subtype。
注意:在Android5.0(API level 21)之前,switchToNextInputMethod()還不知道supportsSwithcingToNextInputMethod屬性。如果用戶切換到某個輸入法而沒有切換關鍵字,他將會得到錯誤,而且可能無法輕易地切換出去。
在你實現一款輸入法的時候還有幾件事需要考慮:
Android項目具有其自身的結構規范,完好的遵循結構規范,可以讓開發事半功倍。下圖分別從Android視圖和Project視圖展示了Android項目的項目結構:圖中左
PS:寫一發關於Activity的生命周期,也算是面試的重點內容. 學習內容:1.Activity的生命周期2.面對多種情況的時候Activity的生命周期3.
Android - 內容提供者(Content Provider)內容提供者組件通過請求從一個應用程序向其他的應用程序提供數據。這些請求由類 Conten