質問

私は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);
    }
}
.

役に立ちましたか?

解決

OK、私はこの解決策で決済しました:

main()メソッドのリスナーをmain()メソッドに追加されました。

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);
.

親ウィンドウで生成されたすべての子ダイアログで、コンストラクタパラメータとして機能します。InputContextからのEvent Localeが[子]ダイアログのINPUTCONTEXTに接続されています。

はいくらかの良い方法があるかもしれません。

他のヒント

Are you just looking for a way to have any layout change affect your application globally?

If so, one approach is to create a custom listener, have the various components that care about the layout change register their interest in such events, and then fire off a change layout event that triggers the change in all components when it's changes in any one of them.

Another way to do it would be store the layout properties in an object that's accessible to any of the components, and have them update their layout periodically via a timer. This would be less desirable, however, because there would probably be lots of needless updates vs. the "only update on event" mode of operation. I'm guessing users of your application won't be changing their keyboard layout more than once or twice per session (as opposed to every 5 seconds)?

Another, third way, to do this is to have the keyboard layout settings stored at the application level, and loaded at startup. Then, when a keyboard layout change happens, prompt the user to restart the app for the changes to take effect globally.

Yes and no: yggdraa's code of Mar 13 worked fine on Windows but failed on Linux.

There may be no universal solution for Linux at all: no such things as Windows' GetKeyboardLayout() and ActivateKeyboardLayout() there. Some configuration-dependent hacks might be possible though, such as parsing the output of xset (details here) and forcing the layout, say, on key up/down.

In the example above, the input selection code in eventDispatched() gets called too late - when the OS keyboard has already switched back to the system-default US.

A few brute force attempts didn't work either: myParticularJField.setLocale(myForcedLocale) from the field's focus handler gets immediately undone upon the first key press. Same for forcing the top-level (JFrame/JDialog) Locale.

Update:

We have only Windows in production, so making this work under Linux is impractical: too much effort.

Just in case, a byproduct. This correctly determines which layout is currently active: default or alternative ("local"). It can't distinguish among several alternative layouts:

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;
    }

}

Update:

In Ubuntu, the change back to the default layout happens at the X window level (DBus events). A workaround: to turn off separate layouts for each window: Settings => Keyboard => Layouts, uncheck "Separate layout for each window."

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top