JFrame 位置错误 Ubuntu(Unity?)

JFrame wrong location with Ubuntu (Unity ?)

好像Ubuntu有bug(可能只有unity)。 getLocation()getSize() 考虑了 JFrame 的装饰,但 setLocation()setSize() 没有考虑。这会导致奇怪的行为。例如,如果在显示框架并更改尺寸后使用 pack(),则框架将下降 20 像素...

为了说明一个具体的案例,当它变得非常烦人时,我制作了一个 SSCCE。它是一个带有基本 JPanel 的 JFrame。如果拖动面板,JFrame 应该会随之移动。

在Windows下,它按预期工作。在 Ubuntu 下,如果我做 setUndecorated(true) 它也可以正常工作,但如果我让装饰,JFrame 会变得疯狂!

public class Test {
    private static JFrame mainFrame;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                mainFrame = new JFrame("test");
                mainFrame.setSize(300,20);
                mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                mainFrame.setVisible(true);

                Container pane = mainFrame.getContentPane();
                pane.addMouseMotionListener(new MouseMotionListener() {
                    private int posX = 0, posY = 0;

                    @Override
                    public void mouseDragged(MouseEvent e) {
                        int x = e.getX() - posX + mainFrame.getX();
                        int y = e.getY() - posY + mainFrame.getY();
                        mainFrame.setLocation(x, y);
                    }

                    @Override
                    public void mouseMoved(MouseEvent e) {
                        posX = e.getX();
                        posY = e.getY();
                    }

                });

            }
        });
    }
}

我不知道该如何解决。如何获得 windows 装饰的大小?而且我不知道 Ubuntu 涉及哪些版本。如果这只是 Unity 的问题,我什至不知道如何确定我的用户是否在使用 Unity...

有解决方法吗?

编辑: 好的,MadProgrammer 确实提供了更好的代码,但有时仍然会出现错误。我相应地编辑了我的 MouseListener 以跟踪错误:

            pane.addMouseMotionListener(new MouseMotionListener() {
                private int posX = 0, posY = 0;

                @Override
                public void mouseDragged(MouseEvent e) {
                    int x = e.getXOnScreen() - posX;
                    int y = e.getYOnScreen() - posY;
                    mainFrame.setLocation(x, y);
                    System.out.println("drag   :  border ignored / border considered : "+(mainFrame.getY()+e.getY())+"   /   "+e.getYOnScreen());
                }

                @Override
                public void mouseMoved(MouseEvent e) {
                    posX = e.getXOnScreen() - mainFrame.getX();
                    posY = e.getYOnScreen() - mainFrame.getY();
                    System.out.println("move  :  border ignored / border considered : "+e.getY()+"   /   "+posY);
                }
            });

每次2个值相同,就说明下次点击会出现bug。否则,值不同。在其他 OS 上,值始终相同。实际上,它们应该是或者总是相同的,或者总是不同的。我不明白他们为什么有时相等有时不同...

我没有 Ubuntu 来测试,但我在 MacOS 和 Windows

上都使用了类似的东西
import java.awt.Container;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class Test {

    private static JFrame mainFrame;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                mainFrame = new JFrame("test");
                mainFrame.setSize(300, 100);
                mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                mainFrame.setVisible(true);

                Container pane = mainFrame.getContentPane();
                MouseAdapter ma = new MouseAdapter() {
                    private Point offset;

                    @Override
                    public void mouseDragged(MouseEvent e) {
                        if (offset != null) {
                            Point pos = e.getLocationOnScreen();

                            int x = pos.x - offset.x;
                            int y = pos.y - offset.y;

                            System.out.println(x + "x" + y);

                            SwingUtilities.getWindowAncestor(e.getComponent()).setLocation(x, y);
                        }
                    }

                    @Override
                    public void mousePressed(MouseEvent e) {

                        Point pos = SwingUtilities.getWindowAncestor(e.getComponent()).getLocation();

//                      Point pos = e.getComponent().getLocationOnScreen();
                        offset = new Point(e.getLocationOnScreen());
                        System.out.println(pos + "/" + offset);
                        offset.x -= pos.x;
                        offset.y -= pos.y;

                        System.out.println(offset);
                    }
                };
                pane.addMouseListener(ma);
                pane.addMouseMotionListener(ma);
            }
        });
    }
}

这应该适用于装饰和未装饰的 windows,因为它需要组件(在屏幕上)的位置与 windows 当前位置之间的差异。拖动时,它会计算距点击点的移动距离并相应地更新 window 的位置(考虑到点击的原始偏移量)