Androidのアプリ開発関連のブログ

ここ数日ブログ更新してなかったが・・・

ここ数日ブログ更新してなかったが、引き続きテキストエディタ制作を連日やってる。

可能ならテキストの強調表示機能を実装させようと思ってたが、
TextViewの一部だけを色を変えたりとかできるのか?と思ってたが、
TextViewのsetText()やgetText()で扱うCharSequenceは、実はandroid.text.Editable型?
android.text.Editableは、android.text.Spannableをimplementsしてるんだが、Spannableってので文字列中の範囲に対して装飾を設定することができる。
で、TextViewにはこれに従って文字列中の一部を装飾する機能が備わってるみたい。

TextView.getText()はCharSequenceの参照が取得できるだけだから、SpannableやEditableに型変換して、setSpan()すると一部の文字色などが変えれる。


android.text.Spannable.setSpanはAPIドキュメントだと、
setSpan(Object what, int start, int end, int flags)
パット見じゃイミフだが、

startとendは対象Spannable中の範囲開始位置と終了位置。
Javaの文字列範囲系メソッドはみんなそうみたいだが、終了位置はインデックス+1になる。

Object whatはandroid.text.style以下にあるクラスのインスタンスになる。
基本的にはandroid.text.style.CharacterStyleを継承したクラスになると思うが、文字の色を変えたいなら、android.text.style.ForegroundColorSpan。
setSpanに渡す際は、同一のObjectを複数回setSpanすると最後の一個しか有効にならないぽいんで、
setSpan毎にnewでインスタンスを作成するか、
Object.clone()で複製すればいいと思うんだが、JavaのObject.clone()はクラス側で対応させないとダメなんだね。

int flagsは、Spannableがimplementsしてるandroid.text.Spannedの定数。
いろいろ定義されてるんだが、SPAN_EXCLUSIVE_EXCLUSIVEしか使わなかった。
SPAN_EXCLUSIVE_EXCLUSIVEは、例えば全文字列と適用範囲が「あいうえお」だとして、文字が変化した場合に「あかいきうえくお」といった感じで、範囲外には拡張しないが内部は拡張できる感じになる。
これと逆な感じなのがSPAN_INCLUSIVE_INCLUSIVEで「あかいきうえくお」こんな感じになる。


設定済みspanの変化を監視して変化時の処理ができるなら、
SPAN_EXCLUSIVE_EXCLUSIVEとSPAN_INCLUSIVE_INCLUSIVEを交互に隙間なく並べれば変化時の処理で変化範囲の取得が楽かと思ったんだが、
android.text.SpanWatcherでspanの変化を監視できそうな気がするが、使い方がわからなかった・・・
変化時の処理はandroid.text.TextWatcherでやった。

TextWatcherは、
public void afterTextChanged(Editable s){}
public void beforeTextChanged(CharSequence s, int start, int count, int after){}
public void onTextChanged(CharSequence s, int start, int before, int count){}
の3つのメソッドの実装が必要なinterfaceだが、
TextViewのaddTextChangedListener()でリスナーとして登録できる。
TextView.addTextChangedListener(new TextWatcher(){
  public void afterTextChanged(Editable s){
    変化後の処理
  }
  public void beforeTextChanged(CharSequence s, int start, int count, int after){
    変化前の処理
  }
  public void onTextChanged(CharSequence s, int start, int before, int count){
    変化時の処理
  }
});
って感じで、EditTextの文字列が変化した時に処理できる。
afterTextChangedが文字入力確定後でonTextChangedだと確定してない時点で飛んでくると思うんで、処理はafterTextChangedで行ったが、
afterTextChangedだと範囲が取得できないので面倒な感じ。
Editable sやCharSequence sは対象TextViewに表示されている文字列の参照なんで、操作すればTextViewに表示されている文字列も変化する。

今回はテキストを解析して一部を強調表示するわけだが、文字列が変化するたびに全体を解析しなおしてたら遅くなりすぎるから、
変化範囲を取得して最低限の範囲を解析するように作った。


まあ、そんなわけで強調表示機能は実装した。
TextViewの一部を装飾できたとしても、オープンソースのライブラリかなんか使って多数の言語に対応させなきゃならんかと思ってたが、
独自実装の単一解析処理で言語不問で強調表示するようにした。
当然ながら、全言語同一の処理じゃ良い感じに強調表示できない場合もあるが、わりとうまく行ったと思う。
気づいたのは、JavaとかJavaScriptなんかは記号が少なくてうまく強調表示させやすいんだが、Perlなんかは記号類が多くて難しい。
HTMLもうまく強調表示できる感じになったが、CSSが含まれるとダメかな。内包JavaScriptはうまいこといくようにした。