在Mac OSX上,使用Java7模态对话框时存在焦点问题。

8
我一直在验证运行在Mac OSX上的应用程序小程序的摇摆应用程序。在此验证期间,我发现模态对话框存在以下问题:
  1. 当打开对话框并设置SetModal(True)时,它会阻止根窗口的内容,但是如果您在根窗口上单击某个位置,则对话框会在其下面,但它应该保持在根窗口的顶部。
  2. 如果对话框有一个JTextInputField,即使您单击它,它也不会获得焦点。
所以我创建了一个小程序来展示这个问题。请帮助我理解这里的问题出在哪里?
package com.macosx.tests;

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

import javax.swing.*;

public class DialogExample extends JApplet{

    private static final long serialVersionUID = 1L;
    private JPanel panel;
    private JButton openDialogBtn;

    private void doStart() {
        panel = new JPanel();
        panel.setPreferredSize(new Dimension(500,500));

        openDialogBtn = new JButton("open dialog");
        openDialogBtn.addActionListener(new ActionListener(){

            @Override
            public void actionPerformed(ActionEvent arg0) {
                ModalDialog dialog = new ModalDialog(panel, true);
                dialog.setVisible(true);
            }

        });
        panel.add(openDialogBtn);
        setContentPane(panel);
    }


    class ModalDialog extends JDialog {
        private static final long serialVersionUID = 1L;

        public ModalDialog(Component parent, boolean modal) {
            Dimension dimensionParentFrame = parent.getSize();
            setSize(new Dimension((parent == null) ? 300 : dimensionParentFrame.width / 2, 75));

            setModal(modal);
            setModalityType(ModalityType.APPLICATION_MODAL);

            JTextField txtField = new JTextField();
            add(txtField, BorderLayout.CENTER);
        }
    }


    @Override
    public void start() {
        try {
            SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                    doStart();
                }
            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

使用上述方法创建一个.jar文件(test.jar)。完成后,创建一个包含以下内容的html文件:
<html>
<head>
<title>Dialog test Applet</title>
</head>
<body>
<applet id="DialogTestApplet" height="800" width="600"
  code="com.macosx.tests.DialogExample"
  archive="test.jar">
</applet>
</div>
</body>
</html>

完成后,运行html文件。你会看到一个灰色背景和一个按钮的小程序。然后尝试:
  1. 点击按钮打开对话框。然后在灰色区域的某个地方单击:对话框会进入浏览器窗口下面,但应该仍然保持在顶部,对吗?
  2. 点击按钮打开对话框。然后单击对话框的文本字段并尝试输入一些内容:文本对话框无法获得焦点。
所以,我在这里做错了什么?能否有一位使用Mac电脑的人来测试一下?
谢谢。
规格:
java.vendor    Oracle Corporation
java.version   1.7.0_07
os.name        Mac OS X
os.version     10.7.4

browser        firefox 15

注意:请注意,这仅在应用程序小程序在浏览器上运行且仅在Mac OSX上时发生。


无法在10.5/1.6上复现。 - trashgod
对我来说,在使用10.7/1.6.0_33时,我仍然看到模态问题(1),但没有看到焦点问题(2)。 - Luis Gonçalves
类似的问题,但是在Ubuntu上。stackoverflow - user1307657
我上周报告了这个 bug,但我只提到了 mac osx。在这里检查:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7196264。也许你应该投票/评论看看他们是否会采取行动。优先级很低,因为他们已经更改了它... - Luis Gonçalves
好消息!我刚刚在Oracle中输入了错误链接。它们与他们拥有的其他开放问题相关8001161。这个问题已经解决,将在即将到来的7u14版本中提供。 - Amos N.
所有这些错误似乎只与焦点问题有关,但对我来说最重要的问题是对话框在浏览器后面打开(即JFileChooser)。那是同一个错误吗? - Nicolas Mommaerts
9个回答

6
我找到了另一种解决方法。当窗口打开时,显示一个选项对话框并在几毫秒后关闭。这将把焦点转移到选项对话框上,然后再返回到对话框,从而可以忽略错误。
将以下代码片段添加到您的对话框构造函数中,它应该可以正常工作:
addWindowListener(new WindowAdapter(){
public void windowOpened(WindowEvent e){
    JOptionPane pane = new JOptionPane();
    final JDialog dialog = pane.createDialog("Please Wait");
    Timer timer = new Timer(50, new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            dialog.dispose();
        }
    });
timer.setRepeats(false);
timer.start();
dialog.setVisible(true);
}

2
你可以尝试使用无形对话框:final JDialog dialog = new JDialog(); dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); dialog.setUndecorated(true); dialog.setModal(true); - xmedeko

1

我在Mac上使用Java 7更新9版本的Safari和Firefox时遇到了同样的问题。当我打开一个包含JTextField的JDialog时,JTextField是无法访问的。

不过我找到了解决方案。我在用户按下“显示对话框按钮”到执行显示按钮代码之间插入了延迟。

例如:

ActionListener al = new ActionListener(){
    public void actionPerformed(ActionEvent ae){
        TitleDialog dialog = new TitleDialog(main.findParentFrame()); // My JDialog which contains a JTextField.
        dialog.setVisible(true);
    }
};
javax.swing.Timer timer = new javax.swing.Timer(1000, al);
timer.setRepeats(false);
timer.start(); 

我发现如果延迟时间太短,解决方案将无法正常工作。

如果使用SwingUtilities.invokeLater而不是javax.swing.Timer,它将无法正常工作。也许SwingUtilities.invokeLater的延迟时间太短了。


1

你应该在你的ModalDialog上放置一个“owner”窗口。为此,在你的ModalDialog构造函数中必须调用super(owner),并且你可以使用SwingUtilities.getWindowAncestor(parent)检索你组件的父窗口parent


谢谢您的建议。我刚刚尝试了您提到的方法,但结果还是一样... - Luis Gonçalves
@LuisGonçalves 尝试发布一个SSCCE,而不必使用小程序。 - Guillaume Polet
为什么不把那段代码复制到Eclipse中,然后选择“Run As -> Java Applet”呢?这样就可以运行小程序并查看其运行情况了。但正如我在注释中提到的那样:只有在浏览器中运行小程序时才会出现这个问题,而这恰恰是我感兴趣的部分。 - Luis Gonçalves

1
  • 我不是Mac/OSX用户,但这是关于FocusJDialog的常见问题,

  • 在运行时创建JDialog时会出现其他问题,

  • Focus基于来自Native OS的属性是异步的

  • 只需创建此JDialog一次,并将此container用于另一个操作

  • JDialog#setVisible也应该包装在invokeLater()

  • 可以通过将JTextField#setText(JTextField#getText())包装在invokeLater()中来强制执行Focus

  • Dialog Focus,这是@camickr的一个很好的解决方法


我将进行一些代码更改,以包括DOS建议,看看它们是否解决了问题。只是提一下,如果在Windows上运行相同的代码,就不会出现任何问题。 - Luis Gonçalves
@Luis Gonçalves,这个常见问题在Win、**nix和OSX平台上的大多数复杂GUI中都会出现,只有一些hack方法可以帮助你解决。 - mKorbel
你真的在 Windows 上运行了吗?因为,如果我在我的 Windows 机器上运行它,我就看不到任何问题了... - Luis Gonçalves
部分同意只在容器中有一个JComponents,但我谈论的是最复杂的GUI(请仔细阅读camickr链接代码中的描述),包含大量的JComponents,您需要将焦点设置为其中一些JTextFieldJButton,默认情况下为第1个。 JComponent是可聚焦的,重要线索是如果JTextField#setText(JTextField#getText())AncesorListener有效,因为这个问题来自于Java5 - mKorbel
如果这是为了复杂的GUI,为什么在这个小例子中不起作用?老实说,我认为这是JRE7在Mac OS X上的一个bug。例如,我尝试了Camickr给出的使用JOptionPane的示例。他说:“在这种情况下,如果您运行上面的代码,您会注意到焦点在“Yes”按钮上”。只有当您在浏览器之外运行applet时,这才是真的。当您在浏览器上运行applet时,创建的对话框的任何组件都没有焦点。 - Luis Gonçalves
  1. 看看Sun BugParade,如果以上建议都不起作用的话。
  2. 还有另一个问题,大多数缺陷都与使用的外观有关。
  3. 你尝试过将quaqua更改为systemLookAndFeel或MetalLookAndFeel吗?当然,这些缺陷也与Bug有关 :-)
- mKorbel

1

我确认,在OS X上运行的JDK7中,我遇到了与旧小程序相同的错误。正如发帖者所提到的,只有在浏览器(ff)中运行小程序时才会出现此错误,而在小程序查看器中则不会。


谢谢你的报告。顺便问一下,你能看一下https://dev59.com/ymfWa4cB1Zd3GeqPk9Uc吗?我也创建了一个似乎是个bug。你能确认一下吗? - Luis Gonçalves

1
我可以确认这是Java 1.7 Update 7+在运行Mountain Lion上的Safari 6和Firefox上的一个问题。奇怪的是,在Lion上运行的早期版本的Safari上它不是一个问题,但在旧操作系统上的Firefox上却是一个问题。我非常渴望找到解决这个问题的方法,因为我的一些小应用程序用户使用的是Mac。我发现的一个解决方法(远远不够)是将窗口最小化,然后重新打开它。然后文本字段/文本区域就变得可编辑了。希望我们能找到一个更好的解决方案来避免这个烦人的要求。

0

我已经找到了解决方案。

GetDirectory varGetDirectory = new GetDirectory(new JFrame(),true);
varGetDirectory.setVisible(true);       

GetDirectory是包含JFileChooser的JDialog。

奇怪的是,所有的JDialog对象都应该使用new JFrame()作为父窗口进行调用,否则从一个父窗口点击,会将顶部模态JDialog向后移动,并且它不能再置于顶部。

我的问题与上述相同。当我从另一个JDialog创建JDialog时,新对话框出现在其他对话框后面。

为了将其置于顶部,我已将所有JDialog的父级设置为上述描述,并且按照预期工作。


0

0

我想使用上述的解决方法(从对话框中打开对话框),但是不显示任何对话框。 这里有一个用于不可见对话框的代码。

final JDialog dialog = new JDialog();
dialog.setUndecorated(true);
dialog.setSize(0, 0);
dialog.setModal(true);
dialog.pack();

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