Java Swing - 当JFrame窗口最大化时,上下文菜单中的鼠标指针会“偏移”。

4

在最大化JFrame时,Swing处理鼠标位置遇到了奇怪的行为:

当我执行这个非常简单的代码时...

public class Test {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();

                JMenuBar menubar = new JMenuBar();
                JMenu menu = new JMenu("File");
                menu.add(new JMenuItem("New"));
                menubar.add(menu);
                frame.setJMenuBar(menubar);

                frame.setSize(200, 200);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}

我通常可以点击 File (第一次点击-按下,释放)-> New(第二次点击)。但是当我最大化JFrame并点击 File 时,上下文菜单会立即在鼠标释放时消失。

此外,当我按住鼠标按钮以防止其消失时,我必须将鼠标移动得更远才能聚焦到 New 项目。

mouse shift

红点表示我必须移动鼠标的区域(或多或少),以便在按下 File 并按住鼠标按钮后聚焦到 New

当右键单击来自JFreeChart的图表时,我观察到了相同的行为。

我认为这是JDK问题,因为我使用了Oracle的JDK,但安装OpenJDK后结果相同。

有人观察到这种奇怪的行为吗?还是我错过了一些明显的东西?

我使用:

  • 1.7.0_147-icedtea(或java-7-oracle的1.7.0_04)
  • OpenJDK Runtime Environment(IcedTea7 2.0)(7〜b147-2.0-0ubuntu0.11.10.1)
  • OpenJDK 64位Server VM(构建21.0-b17,混合模式)
  • Linux Mint 12(lisa)GNOME 3.2.1

1
对我来说,这只是Java 7的一个bug,没有其他问题。试着回到之前的版本,就不会有这个bug了。Java 7有很多bug,就像你刚才提到的那个一样 :( - nIcE cOw
3个回答

3

是的 - 正如 @nIcE cOw 所提到的,这是JDK7中的一个错误。

我已经安装了JDK6,我无法重现这个错误。

java version "1.6.0_23"
OpenJDK Runtime Environment (IcedTea6 1.11pre) (6b23~pre11-0ubuntu1.11.10.2)
OpenJDK 64-Bit Server VM (build 20.0-b11, mixed mode)

2
当需要使用Oracle Java 7(例如使用JavaFX)时,还有一个解决方法。只需将以下代码添加到您的窗口/框架类中即可:

        if (Arrays.asList("gnome-shell", "mate", "other...").contains(System.getenv("DESKTOP_SESSION"))) {
        try {
            Class<?> xwm = Class.forName("sun.awt.X11.XWM");
            Field awt_wmgr = xwm.getDeclaredField("awt_wmgr");
            awt_wmgr.setAccessible(true);
            Field other_wm = xwm.getDeclaredField("OTHER_WM");
            other_wm.setAccessible(true);
            if (awt_wmgr.get(null).equals(other_wm.get(null))) {
                Field metacity_wm = xwm.getDeclaredField("METACITY_WM");
                metacity_wm.setAccessible(true);
                awt_wmgr.set(null, metacity_wm.get(null));
            }
        }
        catch (Exception x) {
            x.printStackTrace();
        }
    }

这段代码片段基于Netbeans开发人员的解决方法

如果桌面会话是 Gnome Shell 或 Mate 会话,则将当前 AWT 窗口管理器类设置为用于 Metacity WM 的类。 - problemzebra
1
好的。那么为什么要检查OTHER_WM的值呢? - Tommy Herbert

0
我想要补充一下problemzebra提供的解决方案。
在Linux上(使用Cinnamon桌面环境),即使是在Java 6(更新45)上,任何Swing应用程序仍然会出现这个问题。
每次移动窗口或调整窗口大小时,问题都会再次出现,因此您需要每次窗口更改时重新应用解决方法。我创建了以下类,并在创建新窗口时使用它:
class LinuxWindowFix implements WindowStateListener {

    private final String desktop;
    private Field metacity_wm;
    private Field awt_wmgr;
    private boolean applyFix;

    private static LinuxWindowFix instance = new LinuxWindowFix();

    public static LinuxWindowFix getInstance() {
        return instance;
    }

    private LinuxWindowFix() {
        applyFix = false;

        List<String> linuxDesktops = Arrays.asList("gnome-shell", "mate", "cinnamon"); //add more desktop names here.

        desktop = System.getenv("DESKTOP_SESSION");
        if (desktop != null && linuxDesktops.contains(desktop.toLowerCase())) {
            try {
                Class<?> xwm = Class.forName("sun.awt.X11.XWM");
                awt_wmgr = xwm.getDeclaredField("awt_wmgr");
                awt_wmgr.setAccessible(true);
                Field other_wm = xwm.getDeclaredField("OTHER_WM");
                other_wm.setAccessible(true);
                if (awt_wmgr.get(null).equals(other_wm.get(null))) {
                    metacity_wm = xwm.getDeclaredField("METACITY_WM");
                    metacity_wm.setAccessible(true);
                    applyFix = true;
                }
            } catch (Exception ex) {
                //ignore
            }
        }
    }

    @Override
    public void windowStateChanged(WindowEvent e) {
        try {
            awt_wmgr.set(null, metacity_wm.get(null));
        } catch (Exception ex) {
            //ignore
        }
    }

    public void apply(Window w) {
        if (!applyFix) {
            return;
        }
        w.removeWindowStateListener(this);
        w.addWindowStateListener(this);
    }
}

只需为您创建的每个窗口调用此函数,它将按预期工作。

LinuxWindowFix.getInstance().apply(myWindow);

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