7

テキスト コントロールを含む子ダイアログを生成する Java Swing アプリケーションがあります。問題は、子ダイアログでキーボード レイアウトを変更すると、ダイアログが閉じられた直後に元に戻ることです。

私が必要としているのは、メイン フレームで切り替えられたか、子フレームで切り替えられたかにかかわらず、切り替えられた後もキーボード レイアウトが維持されることです。

問題を示す SSCCE を次に示します。

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class InheritInputContext {

    public static void main(String[] arg) {
        final MainFrame mainFrame = new MainFrame();
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                mainFrame.setPreferredSize(new Dimension(300, 400));
                mainFrame.pack();
                mainFrame.setLocationRelativeTo(null);
                mainFrame.setVisible(true);
            }
        });

    }
}


class MainFrame extends JFrame {

    MainFrame() {
        setLayout(new BorderLayout());
        JTextArea textArea = new JTextArea();
        add(textArea, BorderLayout.CENTER);

        JButton dialogBtn = new JButton("Dialog");
        add(dialogBtn, BorderLayout.SOUTH);
        dialogBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                ChildDialog cd = new ChildDialog(MainFrame.this);
                cd.setPreferredSize(new Dimension(200, 200));
                cd.setLocationRelativeTo(MainFrame.this);
                cd.pack();
                cd.setVisible(true);
            }
        });
    }
}


class ChildDialog extends JDialog {

    ChildDialog(Window w) {
        super(w);
        JTextArea textArea = new JTextArea();
        getContentPane().add(textArea);
    }
}
4

3 に答える 3

3

わかりました、私はこの解決策で解決しました:

次のように、main() メソッドの Java ツールキットにリスナーを追加しました。

AWTEventListener awtWindowListener = new AWTEventListener() {
    @Override
    public void eventDispatched(AWTEvent event) {
        if (event instanceof WindowEvent) {
            if (WindowEvent.WINDOW_CLOSED == event.getID()
                    || WindowEvent.WINDOW_CLOSING == event.getID()) {
                Window child = ((WindowEvent) event).getWindow();
                Window parent = SwingUtilities.getWindowAncestor(child);
                if (parent == null) return;
                InputContext childIC = child.getInputContext();
                parent.getInputContext().selectInputMethod(childIC.getLocale());
            }
        }

    }
};

Toolkit.getDefaultToolkit().addAWTEventListener(awtWindowListener, AWTEvent.WINDOW_EVENT_MASK);

親ウィンドウをコンストラクターパラメーターとして生成されたすべての子ダイアログで機能します。close イベントで、子ダイアログの InputContext からの Locale が親ウィンドウの InputContext に配置されます。

もっと良い方法があるかもしれませんが。

于 2012-03-13T07:30:34.723 に答える
1

はい、いいえ: 3 月 13 日の yggdraa のコードは、Windows では問題なく動作しましたが、Linux では失敗しました。

Linux の普遍的な解決策はまったくないかもしれません。Windows の GetKeyboardLayout() や ActivateKeyboardLayout() のようなものはありません。ただし、xset の出力の解析 (詳細はこちら) や、キーの上下などでレイアウトを強制するなど、構成に依存するいくつかのハックが可能である可能性があります。

上記の例では、eventDispatched() の入力選択コードが呼び出されるのが遅すぎます (OS キーボードが既にシステムのデフォルトの US に切り替えられている場合)。

フィールドのフォーカス ハンドラからの myParticularJField.setLocale(myForcedLocale) は、最初にキーを押すとすぐに取り消されます。トップレベル (JFrame/JDialog) ロケールを強制する場合も同じです。

アップデート:

実稼働環境には Windows しかないため、これを Linux で機能させるのは現実的ではありません。労力がかかりすぎます。

念のため、副産物。これにより、現在アクティブなレイアウト (デフォルトまたは代替 (「ローカル」)) が正しく判断されます。いくつかの代替レイアウトを区別できません。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class LinuxKeyboardLayoutStatus {

    public enum LayoutType { DEFAULT, LOCAL }

    public LinuxKeyboardLayoutStatus.LayoutType getCurrentKeyboardLayoutType() throws IOException, InterruptedException {
        String[] command = createCommand();
        Process p = Runtime.getRuntime().exec(command);
        BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
        String l = r.readLine();
        r.close();
        p.waitFor();
        return decodeLayoutType(l);
    }

    protected String[] createCommand() {
        return new String[] { "/bin/sh", "-c", "xset -q | grep LED | awk '{ print $10 }' | cut -c5" };
    }

    protected LinuxKeyboardLayoutStatus.LayoutType decodeLayoutType(String commandOutput) {
        return
            commandOutput != null && !commandOutput.equals("0") ? LayoutType.LOCAL : LayoutType.DEFAULT;
    }

}

アップデート:

Ubuntu では、デフォルト レイアウトへの変更は X ウィンドウ レベルで発生します (DBus イベント)。回避策: ウィンドウごとに個別のレイアウトをオフにするには: [設定] => [キーボード] => [レイアウト] で、[ウィンドウごとに個別のレイアウト] のチェックを外します。

于 2012-09-13T10:06:08.350 に答える
1

レイアウトの変更がアプリケーション全体に影響を与える方法を探しているだけですか?

その場合、1 つのアプローチは、カスタム リスナーを作成し、レイアウトの変更を気にするさまざまなコンポーネントにそのようなイベントへの関心を登録させてから、いずれかのコンポーネントが変更されたときにすべてのコンポーネントの変更をトリガーするレイアウト変更イベントを発生させることです。そのうちの。

別の方法として、どのコンポーネントからもアクセス可能なオブジェクトにレイアウト プロパティを保存し、タイマーを介して定期的にレイアウトを更新する方法があります。ただし、「イベント時にのみ更新」モードの操作と比較して、多くの不要な更新が行われる可能性があるため、これはあまり望ましくありません。あなたのアプリケーションのユーザーは、セッションごとに (5 秒ごとではなく) 1 回または 2 回しかキーボード レイアウトを変更しないと思いますか?

これを行うもう 1 つの 3 番目の方法は、キーボード レイアウトの設定をアプリケーション レベルで保存し、起動時にロードすることです。次に、キーボード レイアウトの変更が発生したときに、変更をグローバルに有効にするためにアプリを再起動するようユーザーに促します。

于 2012-03-12T14:20:09.123 に答える