android IME框梳理

來源:互聯網
上載者:User

/frameworks/base/services/java/InputMethodManagerService.java

這是整個系統當中,一切與IME有關的地方的總控制中心。它通過管理下面三個模組來實現系統的IME架構。

1、/frameworks/base/services/java/WindowManagerService

負責顯示IME,接收使用者事件。

2、/frameworks/base/core/java/android.inputmethodservice/InputMethodService

IME內部邏輯,鍵盤配置,選詞等,最終把選出的字元通過commitText提交出來。要做一個像搜狗IME這樣的東西的話,主要就是在這裡做文章。

3、InputManager

由UI控制項(View,TextView,EditText等)調用,用來操作IME。比如,開啟,關閉,切換IME等。

下面說一下InputMethodManagerService這個控制中心是怎麼樣與三個模組互動的。

1、與WindowManagerSerivce的互動。

首先,InputMethodManagerService在初始化時,會調用IWindowManager.Stub.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE)),得到IWindowManager這個代理,然後通過IWindowManager與WindowManagerService互動。比如下面這些操作:

調用mIWindowManager.addWindowToken(mCurToken, WindowManager.LayoutParams.TYPE_INPUT_METHOD),讓WindowManagerService顯示IME介面。

調用mIWindowManager.removeWindowToken(mCurToken)讓IME介面關閉。

調用mIWindowManager.inputMethodClientHasFocus(client)判斷IME是否聚焦。

2、與InputMethodService的互動。

InputMethodManagerService在內部維護著一個ArrayList<InputMethodInfo> mMethodList。這個列表會在服務啟動時通過PackageManager查詢當前系統中的IME程式來得到。與之對應的,每一個IME程式的AndroidManifest.xml中都會有一個Service,而每個Service中都會有標記來告訴系統,自己是個IME程式。下面這個是我從系統內建的例子Samples/SoftKeyboard/AndroidManifest.xml中的取出來的:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
        package="com.example.android.softkeyboard">
    <application android:label="@string/ime_name">
        <service android:name="SoftKeyboard"
                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>
    </application>
</manifest>

另外,InputMethodManagerService內部還有一個PackageReceiver,當系統中有程式的安裝、刪除、重啟等事件發生時,會更新mMethodList。InputMethodManagerService開啟,關閉,切換IME時,其實就是在操作mMethodList中某個InputMethodInfo。把InputMethodInfo中的代表某個IME的InputMethodService啟動或者銷毀,就實現了IME的開啟和關閉。

3、與InputMethodManager的互動

InputMethodManager中會包含一個IInputMethodManager,這個東西就是InputMethodManagerService的代理,開啟關閉IME這些操作就是由InputMethodManager中的某些方法調用IInputMethodManager中相應的方法來實現的。比如:

mService.getInputMethodList()擷取IME列表。

mService.updateStatusIcon(imeToken, packageName, iconId)更新IME表徵圖,即螢幕上方狀態列中的IME表徵圖。

mService.finishInput(mClient)隱藏當前IME。這所以不說關閉IME,是因為IME服務啟動起來以後,只有在系統關閉或者切換IME時才會關閉。

mService.showSoftInput(mClient, flags, resultReceiver)開啟當前IME。

分別介紹完三大模組之後,再介紹兩個東西,IME的實現和怎麼樣調用IME。

1、以系統的SoftKeyboard為例,實現一個IME至少需要Keyboard,KeyboardView,CandidateView,SoftKeyboard這四個東西。

CandidateView負責顯示軟鍵盤上面的那個候選地區。

Keyboard負責解析並儲存鍵盤配置,並提供選詞演算法,供程式運行當中使用。其中鍵盤配置是以XML檔案存放在資源當中的。比如我們在漢字IME下,按下b、a兩個字母。Keyboard就負責把這兩個字母變成爸、把、巴等顯示在CandidateView上。

KeyboardView負責顯示,就是我們看到的按鍵。

上面這兩人東西合起來,組成了InputView,就是我們看到的軟鍵盤。

SoftKeyboard繼承了InputMethodService,啟動一個IME,其實就是啟動一個InputMethodService,當SoftKeyboardIME被使用時,啟動就會啟動SoftKeyboard這個Service。InputMethodService中管理著一個繼承自Dialog的SoftInputWindow,而SoftInputWindow裡面就包括了InputView和CandidateView這兩個東西。

2、怎麼樣調用IME呢?

說起這個東西,很自然地想起EditText來,我們團隊跟蹤過這個Widget,EditText本身很簡單,主要的代碼在TextView和View當中。這兩個Widget本身又很複雜,雜在一起說不清楚。這裡我就把我們團隊以前做過的一個小例子拿進來做參考,說明一下如何從一個View上調用IME和如何接收IME傳過來的字串。

小例子的起源來自於我們要做一個瀏覽器,需要在SurfaceView來在Canvas上面繪製自己需要的東西,開啟自己的主控制迴圈線程,事件處理等。比如我要在SurfaceView上繪製輸入瀏覽器中的按鈕、文本、圖片、輸入框等,當然這些和ImageView,TextView沒有關係,都是用自己的UI引擎來做的。最後所有問題都解決了,卻在輸入框上卡殼了。因為要實現輸入,得調用EditText,否則就必須自己去和EditText一樣串連IME。以前找過相關資料,看網上也有人碰到過這個問題,但都沒有答案。最後,還是團隊中一個很牛的娃給解決了。代碼很簡單,不出二十行,但沒資料,View的源碼又太龐大,費的勁卻是只有我們團隊的人才能體會得到的。。。這裡佩服張老二同學一下,沒有他的努力,就沒有下面這二十多行很重要很重要的源碼的誕生。

首先,定義一個繼承自BaseInputConnection的類。

public class MyBaseInputConnection extends BaseInputConnection{
public MyBaseInputConnection(View targetView, boolean fullEditor) {
super(targetView, fullEditor);
}

public static String tx="";
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {//IME程式就是通過調用這個方法把最終結果輸出來的。
tx = text.toString();
return true;
}
}

BaseInputConnection相當於一個InputMethodService和View之間的一個通道。每當InputMethodService產生一個結果時,都會調用BaseInputConnection的commitText方法,把結果傳遞出來。

public class MyView extends SurfaceView ...{
InputMethodManager input = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);//得到InputMethodManager。
ResultReceiver receiver = new ResultReceiver(new Handler() {//定義事件處理器。
public void handleMessage(Message msg) {
}

});
... ...

input.showSoftInput(this, 0, mRR);//在你想呼出IME的時候,調用這一句。
... ...
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {//這個方法繼承自View。把自訂的BaseInputConnection通道傳遞給InputMethodService。
return new MyBaseInputConnection(this, false);
}
}

低級介面上面,自己調用IME並接收IME的輸出結果,就是這樣的。

下面我想提一下和這個話題相關的另外一件事,就是前面解決過的一個Bug:

http://blog.csdn.net/a345017062/archive/2011/01/04/6116305.aspx

通過這個問題,可以看出WebView上面的IME是如何?的。簡單來說,WebView就是一個ViewGroup,它裡面有兩層,上層是一個EditText,下層是瀏覽器頁面。當瀏覽器的輸入框被使用者點中,需要顯示IME時,就把上層EditText的位置移到瀏覽器的輸入框的位置,高速好EditText的大小和樣式後,讓EditText和瀏覽器頁面融為一體,效果就很好了。

通常來說,這個方式應該比自己調用IME要好些。可以少做很多事。不過,如果產品經理是個很有想像力的人的話,你就不能滿足他設計出來的有可能極端變態卻非常炫的輸入效果了

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.