swingにおける日本語入力時にUIが崩れる不具合と解決方法


はじめに

ここ最近、swing関連のバグ?に悩まされていました。

例えば、このようにJTextFieldやJTextAreaなどのJTextComponent系コンポーネントを用意したとします。
半角英数字を入力する分には特に変わりはないのですが、日本語入力をした途端に、


$\Huge{な~ん~な~ん~す~か~こ~れ}$

バグにも程があります。

ググってみるとInputMethodListenerを用いてウィンドウのrepaint()を叩き続ければ良いという解決法を見つけたので、フレームを最小化していない間はコンポーネントへの全角文字入力以降にwhileでフレームのrepaint()を叩き続けるためのバグ修正用クラスを用意。

バグ修正用クラス
final class SwingPaintBugFix extends Thread {
    private Window w;
    private boolean running = true;

    private static SwingPaintBugFix bugfix = new SwingPaintBugFix();

    private SwingPaintBugFix() {}

    private SwingPaintBugFix(Window w) {
        this.w = w;
    }

    public static void of(JTextComponent txtComp, Window w) {
        w.addWindowListener(new WindowAdapter() {
            @Override public void windowIconified(WindowEvent e) {
                bugfix.stopRunning();
            }
            @Override public void windowDeiconified(WindowEvent e) {
                bugfix = new SwingPaintBugFix(w);
                bugfix.start();
            }
        });
        txtComp.addInputMethodListener(new InputMethodListener() {
            @Override public void inputMethodTextChanged(InputMethodEvent e) {
                // 全角文字を受け取る
                bugfix.stopRunning();
                bugfix = new SwingPaintBugFix(w);
                bugfix.start();
            }
            @Override public void caretPositionChanged(InputMethodEvent e) {}
        });
        txtComp.addKeyListener(new KeyAdapter() {
            // 半角文字を受け取る
            @Override public void keyTyped(KeyEvent e) {}
        });
    }

    @Override public void start() {
        new Thread(this).start();
    }

    @Override public void run() {
        while (running) {
            ((Component) w).repaint();
        }
    }

    private void stopRunning() {
        running = false;
    }
}

しかしながら、常にwhileで処理を実行し続けるという行為に抵抗感が否めず、すべてのJTextComponent系コンポーネントにバグ修正用クラスを適用するのも手間がかかるため、抜本的な解決策を探ることとしました。

原因究明

とりあえず数回再起動しても治らず。

Windows Updateで不具合がでないかどうか確認しながら最新版に更新しても駄目。

「あーめんど」と思いながらも、仕方がないのでセーフモードに。セーフモードでは先程のバグが発生しないことを確認したので、「バッググラウンドプロセス」と「サービス」を控えて通常起動に戻す。

「これでもない、これでもない・・・」と、タスクマネージャーからセーフモード以外のタスクを一つづつ終了させ、ついに原因を特定!

「Nahimic Service」というサウンドユーティリティが犯人だったようです。こいつを試しに停止させてみたら見事にバグが直りました。ググってみると、こいつはUI関連以外にもいろいろと競合を起こしているようです。

・・・これならバグ修正クラスなんて作る意味なかったじゃんか。