java - 在 Java 11 和现代 iMac 上 Swing Java2D

标签 java macos java-11 retina-display java-2d

不久前,我编写并使用了一个 Zoom to Mouse Swing 面板,它可以处理突出显示、平移、鼠标缩放、选择等。它非常好。
我今天又去玩了一些,它不起作用。我一头雾水。我知道我有一个工作示例——某处。但是在我的驱动器上搜索,我的实验都没有奏效。我开始尝试让它再次工作。
最终我发现了问题所在。它是 JDK 11 和我的新 iMac 的某种组合。上次我在这方面工作是在我的旧 Mac 上(我可能使用过也可能没有使用过 JDK 11,我不记得了)。
这就是我的应用程序在 JDK 11 中的样子
enter image description here
这就是 JDK 8 的样子(这就是它应该的样子)
enter image description here
如果您使用代码,您会发现在 JDK 11 下显然存在某种(2X?)扩展,但在 JDK 8 下没有。我不知道它是否试图弥补上的大显示我的机器,还是怎么回事。
但是您尝试在 JDK 11 下进行缩放或平移,发现它没有以鼠标为中心,以及跟踪突出显示是如何错误的,等等。
如何在 JDK 11 下使其正常工作?它是唯一的Mac吗?还是带有“驱动影院尺寸显示器”的 Mac?
下面是代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;

public class TestPanel {

    public static void main(String[] args) throws Exception {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                createAndShowGUI();
            }
        });
    }

    public static void createAndShowGUI() {
        JFrame f = new JFrame("Test Zoom");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLayout(new BorderLayout());
        TestPanelZoom p = new TestPanelZoom();
        f.add(p, BorderLayout.CENTER);
        f.setPreferredSize(new Dimension(400, 500));
        f.pack();
        f.setVisible(true);
    }

    private static class TestPanelZoom extends JPanel {

        boolean hilighted = false;
        private int hiliteX = -1;
        private int hiliteY = -1;
        boolean selected = false;
        private int selectX = -1;
        private int selectY = -1;

        private AffineTransform at = new AffineTransform();

        public TestPanelZoom() {
            setBackground(Color.WHITE);
            setForeground(Color.BLACK);
            addMouseAdapter();
            at.setToIdentity();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 500);
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setTransform(at);
            paintPanel(g2d);
            g2d.dispose();
        }

        private void paintPanel(Graphics2D g2) {
            g2.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setRenderingHint(
                    RenderingHints.KEY_TEXT_ANTIALIASING,
                    RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            Color c = g2.getColor();
            g2.setColor(Color.WHITE);
            g2.fill(getBounds());
            g2.setColor(c);

            for (int i = 0; i < 400; i += 50) {
                Line2D line = new Line2D.Double(i, 0, i, 350);
                g2.draw(line);
                line = new Line2D.Double(0, i, 350, i);
                g2.draw(line);
            }
            if (hilighted) {
                Rectangle2D rect = new Rectangle2D.Double(hiliteX * 50 + 5, hiliteY * 50 + 5, 40, 40);
                g2.setColor(Color.GREEN);
                g2.fill(rect);
            }
            if (selected) {
                Rectangle2D rect = new Rectangle2D.Double(selectX * 50 + 10, selectY * 50 + 10, 30, 30);
                g2.setColor(Color.RED);
                g2.fill(rect);
            }
        }

        private void addMouseAdapter() {
            MouseAdapter ma = new MouseAdapter() {
                int lx, ly;

                @Override
                public void mouseWheelMoved(MouseWheelEvent e) {
                    AffineTransform t = new AffineTransform();
                    double delta = 1 + 0.05f * e.getPreciseWheelRotation();
                    int x = e.getX();
                    int y = e.getY();
                    t.translate(x, y);
                    t.scale(delta, delta);
                    t.translate(-x, -y);
                    t.concatenate(at);
                    at = t;
                    revalidate();
                    repaint();
                }

                @Override
                public void mousePressed(MouseEvent e) {
                    lx = e.getX();
                    ly = e.getY();
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    update(e);
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    update(e);
                }

                @Override
                public void mouseClicked(MouseEvent e) {
                    Point2D srcPt = getSrcPoint(e);
                    selected = true;
                    selectX = (int) (srcPt.getX() / 50);
                    selectY = (int) (srcPt.getY() / 50);
                    revalidate();
                    repaint();
                }

                @Override
                public void mouseMoved(MouseEvent e) {
                    Point2D srcPt = getSrcPoint(e);
                    hilighted = true;
                    hiliteX = (int) (srcPt.getX() / 50);
                    hiliteY = (int) (srcPt.getY() / 50);
                    revalidate();
                    repaint();
                }

                public void update(MouseEvent e) {
                    Point2D srcPt = new Point2D.Double(e.getX(), e.getY());
                    Point2D lastPt = new Point2D.Double(lx, ly);
                    try {
                        at.inverseTransform(srcPt, srcPt);
                        at.inverseTransform(lastPt, lastPt);
                    } catch (NoninvertibleTransformException noninvertibleTransformException) {
                        throw new RuntimeException(noninvertibleTransformException);
                    }
                    double dx = srcPt.getX() - lastPt.getX();
                    double dy = srcPt.getY() - lastPt.getY();
                    at.translate(dx, dy);
                    lx = e.getX();
                    ly = e.getY();
                    revalidate();
                    repaint();
                }

                public Point2D getSrcPoint(MouseEvent e) {
                    Point2D srcPt = new Point2D.Double(e.getX(), e.getY());
                    try {
                        at.inverseTransform(srcPt, srcPt);
                    } catch (NoninvertibleTransformException ex) {
                        throw new RuntimeException(ex);
                    }
                    return srcPt;
                }

            };
            addMouseListener(ma);
            addMouseMotionListener(ma);
            addMouseWheelListener(ma);
        }

    }
}
编辑:
经过更多的探索,无论出于何种原因,在 JDK 11 下,Graphics2D 的默认变换缩放为 2。
在这段代码中,如果你打印出默认的转换,在 JDK 11(在我的环境中)你会得到:
AffineTransform[[2.0, 0.0, 0.0], [0.0, 2.0, 0.0]]
在 JDK 8 上,您将获得身份。
AffineTransform[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();
            System.out.println(g2d.getTransform());
            g2d.setTransform(at);
            paintPanel(g2d);
            g2d.dispose();
        }
我最初通过使用我自己的原始 AffineTransform 来强制它识别。 .在 JDK 8 上,这是一个 NOP,JDK 11,不是那么多。
我试过这个,我第一次将默认转换( at )设置为图形上下文的“默认”(使用它的基本世界观与强制我自己的世界观)。这使网格具有适当的大小,但它会弄乱光标位置。
        // I remove setting `at` up above, and setting it to identity also.
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();
            if (at == null) {
                at = g2d.getTransform();
            }
            g2d.setTransform(at);
            paintPanel(g2d);
            g2d.dispose();
        }
所以这不是一个解决方案。
也许有一种方法可以通过利用上下文的默认转换来使其工作,但我还没有弄清楚。我必须假设默认转换是流畅的,不仅仅是 JDK 到 JDK,而是机器到机器。它甚至可能在多显示器情况下发生变化。
编辑:
继续。
因此,在屏幕上创建和显示的窗口是我在 Java 中要求的大小的 2 倍。我通过创建窗口的屏幕截图来检查这一点。
在 JDK 8 中,让 Java 中的 100 像素线在显示器上变成 200 像素线的机制在我的代码中并不明显。
在 JDK 11 中,可以看到他们试图通过直接使用 AffineTransform 来提升它。
类似地,在 JDK 8 中,鼠标坐标被缩放回 Java 坐标系。因此,如果您在屏幕上放置一个 400x400 的框并将鼠标从一个角移动到另一个角,即使该框的实际像素为 800x800,鼠标的范围也只是 0-400。
JDK 8 中的问题是由于 Transform,屏幕坐标不再与模型坐标匹配。当您绘制一个 400x400 的框时,在屏幕上绘制的是 400x400 乘以 2(由于变换)。但是鼠标坐标不在屏幕坐标中。原始鼠标在 0-400 范围内,而不是 0-800 范围内。因此,您不能再使用图形变换将鼠标坐标转换回模型坐标。幕后隐藏着恶作剧和阴谋诡计。简单地说,转换不是规范的。这就是为什么我的鼠标变得一团糟。
此外,我在另一台分辨率较低的 Mac 上运行了该示例,完全没有问题。 JDK 11 下的默认转换是身份。

最佳答案

我没有看到 JDK 8 和 11 之间的任何区别,但问题是 Graphics2D上下文已经转换为匹配屏幕配置,所以你在哪里做 g2d.setTransform(at)你需要做的g2d.transform(at) .然后一切都会如你所愿。 (我在 OS X 11.4 上使用 JDK 17 运行)
所以paintComponent应该是(我还添加了一些日志记录):

protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();
            AffineTransform t = g2d.getTransform();
            System.out.println("Base G2D:" + t);
            System.out.println("Ours:" + at);
            g2d.transform(at);
            System.out.println("Final:" + g2d.getTransform());

            paintPanel(g2d);
            g2d.dispose();
        }
我已经在 OpenJDK Runtime Environment 18.9 (build 11.0.2+9) 上测试过了和 Java(TM) SE Runtime Environment (build 1.8.0_201-b09) .
我现在也用 11.0.11 进行了测试:
$ rm *.class
$ java -version
openjdk version "11.0.11" 2021-04-20
OpenJDK Runtime Environment AdoptOpenJDK-11.0.11+9 (build 11.0.11+9)
OpenJDK 64-Bit Server VM AdoptOpenJDK-11.0.11+9 (build 11.0.11+9, mixed mode)
$ javac TestPanel.java
$ java TestPanel
Base G2D:AffineTransform[[2.0, 0.0, 0.0], [0.0, 2.0, 0.0]]
Ours:AffineTransform[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
Final:AffineTransform[[2.0, 0.0, 0.0], [0.0, 2.0, 0.0]]

关于java - 在 Java 11 和现代 iMac 上 Swing Java2D,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68514208/

相关文章:

java - 我在 Android Studio 的谷歌地图中得到零经度和纬度值

regex - 为什么 "\b"在查找的正则表达式模式中不起作用?

node.js - Docker 容器中 Sailsjs 应用程序的问题

homebrew - 尝试使用 brew 安装 java11 时出错

java - 带有 jlink 创建运行时的 SSLHandshakeException

java - 在 java 中获取 URL 参数并从该 URL 中提取特定文本

java - 使用 JBoss 7.1.1.Final AS 进行 XML Doctype 验证

java - 迁移 play 项目时纠正 akka 远程设置

macos - SMJobBless 仅适用于 kSMDomainSystemLaunchd,需要 kSMDomainUserLaunchd

java - LogManager.getLogger() 无法确定 Java 11 上的类名