为什么设置JDialog或JFrame的setVisible(true)会切换我的输入法设置?

19

我发现在我的Java Swing应用程序中展示一个JDialog或新的JFrame会导致Windows 7将我的中文输入法从半角模式切换到全角模式。

为什么调用对话框或框架的setVisible(true)方法会切换我的输入法设置?

有人知道代码有什么问题,还是Java的一个bug吗?

复现问题的步骤:

  1. 运行应用程序。
  2. 将您的语言更改为其中一种中文输入法,例如中文(繁体)- 快捷方式
  3. 单击程序中的按钮

我的语言设置

enter image description here

我找到了一个类似的问题Automatic toggling of character width by Windows 7 input methods in Java

并且在添加默认区域设置后,仍然无法解决问题。

import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.Locale;

public class MainWindow {

private JFrame frame;
private Locale l;

/**
 * Create the application.
 */
public MainWindow() {
    initialize();
}

/**
 * Initialize the contents of the frame.
 */
private void initialize() {

    l = new Locale("zh", "zh_TW");

    frame = new JFrame();
    frame.setLocale(l);
    frame.setBounds(100, 100, 450, 300);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JButton btnNewButton = new JButton("New button");
    btnNewButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {

            JDialog d = new JDialog(frame, "Run", true);
            d.getContentPane().add(new JLabel("dsad"));
            d.setMinimumSize(new Dimension(150, 100));
            d.setLocationRelativeTo(null);
            d.setLocale(l);
            d.setVisible(true);

        }
    });
    frame.getContentPane().add(btnNewButton, BorderLayout.CENTER);
}

/**
 * Launch the application.
 */
public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                MainWindow window = new MainWindow();
                window.frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}


}

1
@Andrew Thompson 大多数人,我们默认情况下都是 IDE 的囚犯(有一些增强功能 :-) 包括这个)。 - mKorbel
1
@mKorbel 哦,得了吧。你的IDE不允许你定义自己的模板吗?如果不行,那就换一个更好的吧。 ;) - Andrew Thompson
@AndrewThompson 我已经向Java报告了这个问题,但已经快两周了还没有回复,你能给我建议一些关键词来搜索数据库吗? - user2492632
我现在明白了:前往“区域和语言”对话框的“键盘和语言”选项卡,然后激活“更改键盘...”按钮以获取“文本服务和输入语言”对话框。 - Andrew Thompson
@AndrewThompson 感谢您尝试引起对这个问题的关注。 - user2492632
显示剩余5条评论
4个回答

5
根据Oracle文档,从我对问题的理解来看,它似乎表现出了我所期望的行为:
应用程序的默认区域设置有三种确定方式。首先,除非您已明确更改了默认值,否则 Locale.getDefault() 方法将返回 Java 虚拟机 (JVM) 最初加载时确定的区域设置。也就是说,JVM 从主机环境中确定默认语言环境。主机环境的区域设置由主机操作系统和在该系统上建立的用户首选项确定。
在您的问题中,在应用程序启动后,Windows 更改了默认值,这意味着 JVM 区域设置已经设置。特定框架的区域设置被设置,但创建新框架会基于默认区域设置设置区域设置(这适用于 JDialog 和 JFrame)。
 protected void dialogInit() {
     enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.WINDOW_EVENT_MASK);
     setLocale( JComponent.getDefaultLocale() );
     setRootPane(createRootPane());
     setRootPaneCheckingEnabled(true);
     if (JDialog.isDefaultLookAndFeelDecorated()) {
         boolean supportsWindowDecorations =
         UIManager.getLookAndFeel().getSupportsWindowDecorations();
         if (supportsWindowDecorations) {
             setUndecorated(true);
             getRootPane().setWindowDecorationStyle(JRootPane.PLAIN_DIALOG);
         }
     }
     sun.awt.SunToolkit.checkAndSetPolicy(this, true);
 }

由于您在Windows中设置了默认语言环境,因此人们会认为JComponent.getDefaultLocale()将返回Windows语言环境。 getDefaultLocale()本身返回当前应用程序上下文的语言环境(VM语言环境)。 调用以下方法将为当前上下文设置语言环境:

javax.swing.JComponent.setDefaultLocale(locale);

这会为虚拟机设置默认的语言环境(实际上是调用了

SwingUtilities.appContextPut(defaultLocale, l);

这使得发生的事情稍微更加明显了。

如果设置默认语言环境能够解决问题,我想在调用链的其他地方可能会以某种方式调用默认语言环境,导致Windows更改设置。


如果这不能给你预期的功能,我唯一能想到建议你研究的其他属性是InputContext类:
提供控制文本输入设施(如输入法和键盘布局)的方法。两个方法都处理输入法和键盘布局:selectInputMethod允许客户端组件通过区域设置选择输入法或键盘布局,getLocale允许客户端组件获取当前输入法或键盘布局的区域设置。

这不是正确的答案,因为它只是将语言更改为非 OP 所需的 zh_TW。OP 想要将语言设置为半角宽度,在这个答案描述的任何方法中都无法实现。 - Splash
无论是javax.swing.JComponent.setDefaultLocale(locale);还是InputContext都没有达到我想要的效果,InputContext可以保留键盘布局,但它没有解决自动切换到全角的问题。 - user2492632

4
我认为这可能与Windows 7记住每个运行应用程序的键盘/语言设置有关:
  1. 我打开浏览器(在这种情况下是Chrome)。我可以在任务栏中看到我的键盘设置为ES(西班牙语)。
  2. 然后我打开其他任何应用程序。没有任何变化。
  3. 我再次将焦点设置在浏览器上。
  4. 我按Alt + Shift,这是更改键盘设置的快捷方式。现在我可以看到是EN(英语)
  5. 我单击其他应用程序的窗口(或使用Alt + TAB)。我的键盘设置会更改为ES
  6. 每次回到浏览器时,键盘设置都会更改为EN,并保留其他应用程序的ES设置。

这对于JVM应用程序也适用,例如Eclipse,因此我认为这不是JVM区域设置问题。


1
我也发布了这个问题,但被版主删除了。一些输入法有多个选项,一些窗口是国际版,一些是带有输入法的英文版。我认为这与Java无关。 - Splash
我同意这不是区域设置的问题,我将把项目改为SWT。SWT的对话框没有任何问题。 - user2492632

3

首先,OP的代码存在一个bug。让我来输入正确的代码:

l = new Locale("zh", "TW");

OP指的是半角/全角(不是半字节/全字节)。在全角模式下,英文字母将使用多个字节,例如使用A而不是A。这可以通过Shift-Space进行切换。

输入法简介

在“区域和语言”中,您可以选择语言以及许多键盘。例如,简体中文至少有三种常用的键盘:

  1. 美国键盘允许用户输入英语,无法输入中文

  2. 微软拼音键盘允许用户输入拼音并选择正确的字符

  3. 双拼键盘允许用户用更少的按键输入拼音

  4. 在Windows 7中,还可以添加更多键盘,专门用于简体中文。

够复杂吗?我还有更多要告诉你:在Microsoft Pinyin等键盘下,有模式可供选择Unicode符号或ASCII符号作为逗号、句号和其他标点符号,同时您正在输入中文。是的,我可以先用。,、输入中文单词一段时间,然后我可以切换模式,从那时起,我可以用.,\输入中文

除了在中文输入法中使用美式键盘,微软拼音键盘还有一种只输入英语的模式。所以如果我想要输入英语,我可以使用美式英语语言enter image description here,或者中文语言微软拼音键盘的英语模式enter image description here

enter image description here.

我的回答

OP明确希望切换键盘,甚至在一个键盘下切换模式,而不是切换语言。在Java中,可以通过多种方式设置Locale来更改语言,但无法更改键盘或模式。如果我想让我的Java Swing应用程序切换到具有中文双拼键盘和英文标点符号的模式,以进行中文输入怎么办?

请纠正我如果我错了,但Java无法为特定的IME或模式选择本地键盘。这里有一篇关于Windows甚至Mac上IME开发的文章,希望能解释很多问题。

http://blog.gatunka.com/2009/09/20/ime-basics-for-developers/

但是,如果原帖只是想让用户在密码字段中输入英文,那么他有很多解决方案。但这不是他所说的。


1
我做了一个简单的测试:
我打开了IE,选择了一个标签,在地址栏中将中文输入法设置为半角。然后点击另一个标签,输入法会自动切换到全角。
所以我认为这与Java无关,这是Windows/输入法的行为。

我做了同样的测试,但它没有改变为全角模式,而是从中文半角模式变成了“繁体中文-快速”英文半角模式。在新标签页之前:http://i.imgur.com/ypzI902.jpg 在新标签页之后:http://i.imgur.com/zFCfNuk.jpg - user2492632
这也与Windows区域设置有关。您的默认键盘是什么,用于默认输入语言? - Splash
中文(繁體)美式鍵盤,您可以在問題的第一個圖像中找到它。 - user2492632
我把我的默认键盘改成了中文-快速,但是它还没有变成全角。 - user2492632
所以,如果您的默认键盘是中文美式键盘,那么当您点击一个新标签时,输入法就会变成美式键盘。在您更改了默认键盘后,您可以尝试重新启动计算机,以便在IE新标签和Java应用程序中再次尝试。 - Splash
更改了默认键盘并重新启动电脑后,IE仍然没有改为全宽。而当Java应用程序启动时,它会自动变成全宽。 - user2492632

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接