はじめに
IME APIシリーズ最終回です。
本日はAndroid編です。
ちなみにこれまでの記事は以下の通り。
- IMEを使う(序章)
- IMEを使う(Windows: IMM編)
- IMEを使う(Windows: TSF編)
- IMEを使う(Windows.UI.Text.Core編)
- IMEを使う(macOS編)
- IMEを使う(GTK+編)
- IMEを使う(iOS編)
AndroidのIME API
AndroidのIME APIはぱっと見ではあまり大きな癖もなく、シンプルなものになっています。
ただ、どうやら深い闇の歴史を持っているようで、所々に謎の実装があり、それを表から直接扱えないようになっています(/** @hide */!)。
このため、どこまで自分で実装し、どこからをOSに任せるかという点については十分な調査が必要そうです。
それとTextEditクラスを自作する場合には、他のOSとは一味違った実装が必要です。
BaseInputConnection
まず、AndroidのIME APIの中心はBaseInputConnectionです。
これはInputConnectionを継承したクラスです。
本来ならインタフェースであるInputConnectionを継承したクラスを実装したいところですが、どうにもBaseInputConnectionの実装を踏襲しないと破綻しそうな気がしており、BaseInputConnectionを継承するのが良さそうに見えます。
Editable
BaseInputConnectionを継承すると問題になるのが、文字列クラスです。
InputConnectionの実装なら独自の文字列クラスを直接扱えるのですが、残念なことにBaseInputConnectionの内部ではEditableを通して文字列を操作します。
そのため、Editableのインタフェースで独自の文字列クラスにアクセスできるようにする必要があります。
EditableはCharSequence, GetChars, Spannable (Spanned), Appendableを継承しており、それぞれ
-
CharSequence,GetChars- 文字列へのアクセス
-
Spannable(Spanned)- 文字列の選択範囲の管理
-
Appendable- 文字列を末尾に追加するための関数
-
Editable- 文字列を挿入・置換・削除するための関数
- 入力文字列のフィルタの管理・適用
といったことが実装される必要があります。
最終的な実装方針
-
BaseInputConnectionを継承したクラスを実装する-
Editableを継承した文字列クラスを実装する - その文字列クラスのインスタンスを
public Editable getEditable()で返す- IME APIで重要なテキストの取得や編集周りはほぼこれを返せば実装完了です
- つまり、事実上
Editableの実装です
- ディスプレイ上の文字列レイアウト情報を返す
-
- Viewを実装する
-
public InputConnection onCreateInputConnection(EditorInfo outAttrs)-
outAttrsを適切に設定する - 実装したクラスのインスタンスを返す
-
-
public boolean onCheckIsTextEditor()でtrueを返す
-
おわりに
8日間に渡るIME APIのお話はこれで終わりです。
Windows/macOS/GTK+についてはTATEditorの実装を通しての記憶を頼りに書きました。
一方、iOS/Android/UWPのIME APIは少し前にアプリ版のTATEditorをリリースするために最低限の挙動を確認する際に実装したコードに基づいて書きました。
様々なOSのIME APIを見てきましたが、IMEと適切にやり取りをしてくれるクロスプラットフォームなライブラリの設計は難しそうということがなんとなく感じ取ってもらえれば幸いです。
というのもIME APIがView、描画系、文字列クラスとそれなりの強度で結びついてしまっているためです。
(たぶんそんなライブラリが必要になる場面はほぼないと思いますが)
さて、明日から数日はTATEditorで使用しているライブラリの各OSでのビルド方法やざっくりとした使い方を紹介していきたいと思います。