7

我有一个 Java Swing 应用程序,它生成带有文本控件的子对话框。问题是,当您在子对话框中更改键盘布局时,它会在对话框关闭后立即变回。

我需要的是切换后保留的keboard布局,无论是在主框架还是在子框架中切换。

这是一个说明问题的 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);

它适用于以父窗口作为构造函数参数生成的所有子对话框。在关闭事件时,来自子对话框 InputContext 的 Locale 被放入其父窗口的 InputContext 中。

不过可能有更好的方法。

于 2012-03-13T07:30:34.723 回答
1

您是否只是在寻找一种方法来让任何布局更改在全球范围内影响您的应用程序?

如果是这样,一种方法是创建一个自定义侦听器,让关心布局更改的各种组件注册它们对此类事件的兴趣,然后触发更改布局事件,当任何组件发生更改时触发所有组件的更改其中。

另一种方法是将布局属性存储在任何组件都可以访问的对象中,并让它们通过计时器定期更新其布局。然而,这不太理想,因为与“仅在事件时更新”操作模式相比,可能会有很多不必要的更新。我猜您的应用程序的用户不会在每个会话中更改他们的键盘布局超过一次或两次(而不是每 5 秒一次)?

另一种方法是,将键盘布局设置存储在应用程序级别,并在启动时加载。然后,当键盘布局发生更改时,提示用户重新启动应用程序以使更改全局生效。

于 2012-03-12T14:20:09.123 回答
1

是和否:yggdraa 的 3 月 13 日代码在 Windows 上运行良好,但在 Linux 上失败。

Linux 可能根本没有通用的解决方案:那里没有 Windows 的 GetKeyboardLayout() 和 ActivateKeyboardLayout() 之类的东西。不过,一些与配置相关的 hack 可能是可能的,例如解析 xset 的输出(此处有详细信息)并强制布局,例如按键向上/向下。

在上面的示例中,eventDispatched() 中的输入选择代码被调用得太晚 - 当操作系统键盘已经切换回系统默认的 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 回答