当前位置: 首页 > 知识库问答 >
问题:

在Java11和现代iMac上摇摆Java2D

年凯康
2023-03-14

不久前,我写了一个缩放到鼠标摆动面板,处理高亮、平移、鼠标缩放、选择等。非常好。

我今天又去玩了几次,但都没用。我很困惑。我知道我有一个很好的例子——在某个地方。但在我的驾驶过程中,我的任何实验都不起作用。我开始努力让它再次发挥作用。

最终我发现了问题所在。它是JDK 11和我的新iMac的某种组合。我上一次做这个的时候,是在我的旧Mac上(我不记得在那里我可能用过JDK 11,也不记得有没有用过JDK 11)。

如果您使用代码,您会看到显然有某种(2X?)在JDK 11下进行缩放,但不是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,没有那么多。

我尝试了这个方法,第一次将默认转换()设置为图形上下文的“默认”(使用其基本世界视图与强制自己的世界视图)。这可以使网格具有适当的大小,但会影响光标的位置。

        // 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中,可以看到他们试图通过直接使用仿射变换将其向上移动。

同样,在JDK 8中,鼠标坐标缩放到Java坐标系。所以如果你在屏幕上放一个400x400的盒子,把鼠标从一个角落移动到另一个角落,即使这个盒子的实际像素是800x800,鼠标的范围也只是0-400。

JDK 8中的问题是,由于转换,屏幕坐标不再与模型坐标匹配。绘制400x400方框时,屏幕上绘制的是400x400时间2(由于变换)。但鼠标坐标不在屏幕坐标中。原始鼠标位于0-400范围内,而不是0-800范围内。因此,您不能再使用图形变换将鼠标坐标转换回模型坐标。隐藏在被子里的是恶作剧和诡计。简单地说,转换不是规范的。这就是为什么我的鼠标被弄得一团糟。

另外,我在另一台分辨率较低的Mac上运行了示例,完全没有问题。JDK 11下的默认转换是身份。

共有2个答案

巫培
2023-03-14

为了子孙后代,我想发布最终的代码,以防其他人想要一个工作示例,而不是试图将原始代码与讨论并列。

package pkg;

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();
        }

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

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

            Graphics2D g2d = (Graphics2D) g.create();
            g2d.transform(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);
        }
    }
}
王旺
2023-03-14

我看不出JDK 8和11之间有什么区别,但问题是Graphics2D上下文已经被转换以匹配屏幕配置,所以你在哪里做g2d.setTransform(at)你需要做g2d.transform(at)。然后一切都会像你期望的那样工作。(我在OS X 11.4上使用JDK 17运行)

所以appComponent应该是(我还添加了一些日志记录):

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\u 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]]
 类似资料:
  • 例: “foo”和“bar”可以是任何字符串键,但它们在键集中应该是唯一的。 我知道,使用Swagger,我可以定义一个对象数组,但这给出了一个不同的API,因为那时我们将拥有如下内容: 我已经阅读了“开放API规范”-“添加地图数据类型支持#38”页面。据我了解,它推荐使用additionalProperties,但似乎并没有回答我的需求(或者说与我使用的Swagger UI 2.1.4不兼容)

  • 嗯,这是一个非常新的提示。我声明要在window builder的帮助下自己编写GUI应用程序的代码,我已经决定停止使用netbeans,因为我在这里读到一些人说这很好。你可能认为我没有调查,但相信我,我做了功课。。。 我尝试了甲骨文所说的方式: > 公共类MyClass实现ActionListener{ someComponent。addActionListener(instanceOfMyCl

  • 我们在代理后运行服务,以便: 被路由到公共地址 或者从另一个角度定义: 当nginx在上接收到请求时,它会去掉前缀,并将请求传递给路径上的service。 在设置任何东西之前(使用默认的SpringDoc配置),我可以正确地看到超文本传输协议上的昂首阔步的文档://service-post: 8080/swagger-ui.html。 设置主机上公共地址的路径。com,我正在使用: 然而,这似乎完

  • 理想情况下,我们将有一个显示所有标记为public的控制器/方法的大摇大摆的页面,以及另一个显示所有endpoint的密码安全endpoint。这可能吗?

  • 我有一个java项目(tomcat webapp)和一些REST Api。我想为他们生成大摇大摆的文档。我从本教程(github)开始。我没有maven我们使用蚂蚁任务。我加入了swagger-annotations-1.5.0。jar和所有随swagger jaxrs jar 1.5.0版本附带的jar(如果有用的话,我可以包括一个完整的列表),我已经注释了一些方法,我有一个如下的配置类: }

  • 我们在我们的泽西应用程序中使用了@Role允许注释来限制用户对应用编程接口某些部分的访问。我们如何在SwaggerUI中显示这些信息? 到目前为止,我已经用@ApiOperation注释了方法以显示in/out参数,并尝试使用@Authorization/@AuthorizationScope,但我只为我们不使用的oauth2显示了它。最接近out case的是ApiKeyAuthDefiniti