IMEで変換中の文字か否かを判断する
GUIにおいて入力可能なコンポーネントで桁数制限をかけたり、桁数到達を判定したり、クリップボードからの文字貼付けを監視したりするのは常套の実装であり、C#ではKeyPressイベントの捕捉とネィティブウインドウによる特定のメッセージ捕捉で実現していた。
Java-Swingの場合はJTextComponentがMVCモデルに準拠しており、コンポーネントに設定できるDocumentインタフェース、又はDocumentに設定するDocumentFilterクラスのオーバライドにより、C#よりもスマートに解決できる。
例えばDocumentFilterを拡張する場合は以下のようにinsert, remove, replaceそれぞれの文字列操作イベントに処理を挿入することができる。(取得したDocumentをAbstractDocumentにキャストしなくてはならないのがスマートではないのだが..)
((AbstractDocument)jtextComponent.getDocument()).setDocumentFilter( new DocumentFilter(){ @Override public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException { //文字例が挿入される際の前処理を書く super.insertString(fb, offset, string, attr); } @Override public void remove(FilterBypass fb, int offset, int length) throws BadLocationException { //文字例が除去される際の前処理を書く super.remove(fb, offset, length); } @Override public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrSet) throws BadLocationException { //文字例が置換される際の前処理を書く super.replace(fb, offset, length, text, attrSet); }});
ところがこれらのメソッドに処理を書く際に注意しなくてはならないことがある。それはこの入力コンポーネント上でオペレータがIMEを使って変換作業中の文字操作時も全てこのメソッドが呼ばれることである。
このことは、これらメソッド内で入力された文字の桁数を正しく認識することができない(変換が未確定な文字もカウントされる)ことを意味する。
Swing初心者の私はこれに暫く悩まされたが、以下の方法で解決できることが解った。
@Override
public void replace(FilterBypass fb, int offset, int length,
String text, AttributeSet attrSet) throws BadLocationException {
//変換中だったらその後の処理を実施しない
if ( attrSet != null
&& attrSet.isDefined(StyleConstants.ComposedTextAttribute)){
super.replace(fb, offset, length, text, attrSet);
return;
}
// バリデーションや桁数の判定処理を以下に記述
}
引数のattrSetパラメタにStyleConstants.ComposedTextAttributeが設定されている場合は、まだIME未確定ということだ。
Swingは識者の方々が多く有り難い。StyleConstants.ComposedTextAttributeについては以下のページで解説されていたのをそのまま参考にさせて頂いた。
SwingTips文字列操作編 - 技術日記 - Nikunoki's Web Diary