Android/EditTextでIMEの未確定文字列が確定された瞬間(のフォーク)

Android の EditText の文字列と、String 変数値を同期させたいケースって結構あると思うんですけど、LostFocus みたいなのでやると、オンフォーカスな状態で Activity が閉じられた時に LostFocus が呼ばれなくてあぼんとなるのは VB6あがりのプログラマ(=私)なら誰もが経験するんじゃないでしょうか?

じゃあ onTextChanged だぜ、って仕込んでみると、Android のこれは IME で変換中の文字列もバンバン飛んで来まして大変使い勝手が悪い。(サジェストなんかする際には必要なんでしょうけども)

例えば、以下で紹介されている方法

を実装しますと、IME確定前の文字列もじゃんじゃん同期してくれちゃいます。

これに起因したであろう Android アプリもありまして(Instagram とか。今は治ってる。)、なんとかならんかなーと思っていました。

そんな時、こちら↓

の記事にめぐり逢いまして、まさに私が求めていたそのもの。

ですが、未確定文字=下線付き(UnderlineSpan)である、という前提がどうにもしっくり来ませんで(未確定文字に下線を付けない IME もそりゃあるだろうなーという意味で)。

もちろん、その前で言及されている内部クラス android.view.inputmethod.ComposingText を文字列比較するのもうーん…。

で、自分でも試行錯誤してみたところ、[Spanned.getSpanFlags](http://developer.android.com/reference/android/text/Spanned.html#getSpanFlags(java.lang.Object) というメソッドがあるのに気づきました。あと、SPAN_COMPOSING というフラグも。

これらを使って、文字列が確定されているかどうか?を識別することができるのではないかと考え、前出の記事のコードを以下のように修正してみました。

DetermineComposingText.java

edit1.addTextChangedListener(new TextWatcher()
{
    int currentLength = 0;

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {}

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        currentLength = s.toString().length();
    }

    @Override
    public void afterTextChanged(Editable s) {
        Log.v("", "after:" + s.toString());
        if (s.toString().length() < currentLength) {
            return;
        }
        boolean unfixed = false;
        Object[] spanned = s.getSpans(0, s.length(), Object.class);
        if (spanned != null) {
            for (Object obj : spanned) {
                // UnderlineSpan での判定から getSpanFlags への判定に変更。
                // if (obj instanceof android.text.style.UnderlineSpan) {
                if ((s.getSpanFlags(obj) & Spanned.SPAN_COMPOSING) == Spanned.SPAN_COMPOSING) {
                    unfixed = true;
                }
            }
        }
        if (!unfixed) {
            Toast toast = Toast.makeText(getApplicationContext(), "確定", Toast.LENGTH_SHORT);
            toast.show();
        }
    }
});

s.getSpanFlags(obj) & Spanned.SPAN_COMPOSING) == Spanned.SPAN_COMPOSING) で SPAN_COMPOSING(未確定文字)のフラグが立っているかを判定しています。

とりあえず手持ちの ATOK では修正前と同じように動作しているようです。

IME 側で「未確定文字は SPAN_COMPOSING を必ず設定する」ものかどうかは分かりませんが、個人的には UnderlineSpan を使った手法よりもすっきりしました、というお話でした。