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 を使った手法よりもすっきりしました、というお話でした。