【问题标题】:JPanel gets repainted several times on resize, sometimes not at allJPanel 在调整大小时被重绘了几次,有时根本没有
【发布时间】:2015-05-22 01:58:56
【问题描述】:

首先,一些背景。我正在用 Java 构建我的第一个 GUI 应用程序,但由于我不熟悉 GUI 类,我不确定我是否以正确的方式进行操作。最终目标是根据 Open Street Map 数据构建可调整大小、可缩放、可滚动的地图。

我现在拥有的是 JPanel 的一个子类,我在 JFrame 中调用 LinePanel,并使用 Graphics 对象绘制代表道路的线条。这可以很好地检查我是否正确解析和解释数据,但它似乎不够和幼稚。我已经遇到了一个问题,即 JPanel 在调整大小时被重新绘制多次,导致地图出现错误,以至于我的应用程序需要癫痫警告。

这是我现在的代码:

package map;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class MapDisplay {
    private JFrame frame;
    private LinePanel jpanel;

    public MapDisplay(Map map) {
        this.frame = new JFrame();
        frame.setLayout(new BorderLayout());
        frame.addComponentListener(new ComponentAdapter() {
            public void componentResized(ComponentEvent e) {
                jpanel.repaint();
            }
        });
        jpanel = new LinePanel(map);
        frame.add(jpanel, BorderLayout.CENTER);
    }

    public void display() {
        frame.setSize(710, 935);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jpanel.repaint();
    }

    class LinePanel extends JPanel {
        private static final long serialVersionUID = 1965018056953712219L;
        private Map map;
        private int width;
        private int height;

        private int latAsY(double lat) {
            return height
                    - (int) (height * (lat - map.getMinLat()) / (map
                            .getMaxLat() - map.getMinLat()));
        }

        private int lonAsX(double lon) {
            return (int) (width * (lon - map.getMinLong()) / (map.getMaxLong() - map
                    .getMinLong()));
        }

        private void recalculateDimensions() {
            double mapRatio = (map.getMaxLat() - map.getMinLat())
                    / (map.getMaxLong() - map.getMinLong());
            double panelRatio = this.getHeight() / (double) this.getWidth();
            if (mapRatio > panelRatio) {
                width = (int) (this.getHeight() / mapRatio);
                height = this.getHeight();
            } else {
                width = this.getWidth();
                height = (int) (mapRatio * this.getWidth());
            }
        }

        public LinePanel(Map map) {
            super();
            this.map = map;
        }

        public void repaint() {
            if (map != null) {
                recalculateDimensions();
                Graphics2D g = (Graphics2D) this.getGraphics();
                if (g != null) {
                    g.setStroke(new BasicStroke(2));
                    g.clearRect(0, 0, jpanel.getWidth(), jpanel.getHeight());
                    g.setColor(Color.WHITE);
                    g.fillRect(0, 0, width, height);
                    g.setColor(Color.BLACK);
                    for (String wayId : map.getWays()) {
                        Way way = map.getWay(wayId);
                        Node prev = null;
                        for (String nodeId : way.getNodes()) {
                            Node cur = map.getNode(nodeId);
                            if (prev != null) {
                                int y1 = latAsY(prev.getLatitude());
                                int x1 = lonAsX(prev.getLongitude());
                                int y2 = latAsY(cur.getLatitude());
                                int x2 = lonAsX(cur.getLongitude());
                                g.drawLine(x1, y1, x2, y2);
                            }
                            prev = cur;
                        }
                    }
                }
            }
        }
    }
}

我在调整大小时调用 repaint,因为它不会自动执行此操作,这是我做错了什么的另一个迹象。我还使用 g.fillRect 手动清除 JPanel,因为在重绘地图之前调用 super.repaint() 不会导致任何内容出现...

基本上,我只是想从更高级的 Java 程序员那里得到一些关于我应该如何进行整个工作的指导。如果我走在正确的轨道上,请随意将我推向正确的方向,而不是让我走上新的道路,但我怀疑情况是否如此。

【问题讨论】:

    标签: java swing jpanel awt


    【解决方案1】:
    • public void repaint() {不,不,不
    • Graphics2D g = (Graphics2D) this.getGraphics(); - 不,不,不

    这不是 Swing 中绘画的工作方式。请参阅 Performing Custom PaintingPainting in AWT and Swing 了解有关如何在 Swing 中进行绘画的更多详细信息

    您应该首先摆脱您的 repaint 方法并将其替换为 paintComponent 方法...

    public class MapDisplay {
    
        private JFrame frame;
        private LinePanel jpanel;
    
        public MapDisplay(Map map) {
            this.frame = new JFrame();
            frame.setLayout(new BorderLayout());
            frame.addComponentListener(new ComponentAdapter() {
                public void componentResized(ComponentEvent e) {
                    jpanel.repaint();
                }
            });
            jpanel = new LinePanel(map);
            frame.add(jpanel, BorderLayout.CENTER);
        }
    
        public void display() {
            frame.setSize(710, 935);
            frame.setVisible(true);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            jpanel.repaint();
        }
    
        class LinePanel extends JPanel {
    
            private static final long serialVersionUID = 1965018056953712219L;
            private Map map;
            private int width;
            private int height;
    
            private int latAsY(double lat) {
                return height
                                - (int) (height * (lat - map.getMinLat()) / (map
                                .getMaxLat() - map.getMinLat()));
            }
    
            private int lonAsX(double lon) {
                return (int) (width * (lon - map.getMinLong()) / (map.getMaxLong() - map
                                .getMinLong()));
            }
    
            private void recalculateDimensions() {
                double mapRatio = (map.getMaxLat() - map.getMinLat())
                                / (map.getMaxLong() - map.getMinLong());
                double panelRatio = this.getHeight() / (double) this.getWidth();
                if (mapRatio > panelRatio) {
                    width = (int) (this.getHeight() / mapRatio);
                    height = this.getHeight();
                } else {
                    width = this.getWidth();
                    height = (int) (mapRatio * this.getWidth());
                }
            }
    
            public LinePanel(Map map) {
                super();
                this.map = map;
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                if (map != null) {
                    recalculateDimensions();
                    Graphics2D g2d = (Graphics2D) g.create();
                    g2d.setStroke(new BasicStroke(2));
                    g2d.clearRect(0, 0, jpanel.getWidth(), jpanel.getHeight());
                    g2d.setColor(Color.WHITE);
                    g2d.fillRect(0, 0, width, height);
                    g2d.setColor(Color.BLACK);
                    for (String wayId : map.getWays()) {
                        Way way = map.getWay(wayId);
                        Node prev = null;
                        for (String nodeId : way.getNodes()) {
                            Node cur = map.getNode(nodeId);
                            if (prev != null) {
                                int y1 = latAsY(prev.getLatitude());
                                int x1 = lonAsX(prev.getLongitude());
                                int y2 = latAsY(cur.getLatitude());
                                int x2 = lonAsX(cur.getLongitude());
                                g2d.drawLine(x1, y1, x2, y2);
                            }
                            prev = cur;
                        }
                    }
                    g2d.dispose();
                }
            }
        }
    }
    

    是的,重绘事件可能会在组件被重新调整大小时生成多次,但重绘事件也可以通过RepaintManager 自动减少(即RepaintManager 可能会被调用 100 次以重绘一个组件,可能只会产生 10 个实际的重绘事件——例如)...

    【讨论】:

    • 谢谢,我知道我必须做一些完全错误的事情。我刚刚发现了一堆其他问题,其中其他 noods 做了类似的思考练习,但没有人纠正它们。
    • 为了能够进一步修复它,我们需要查看 runnable example 来证明您的问题。这不是代码转储,而是您正在做的事情的一个例子,它突出了您遇到的问题。这将减少混乱并获得更好的响应
    • 只是好奇,为什么paintComponent优于重绘?
    • 简短回答 - 因为这就是 API 的工作方式。长答案 - repaint 的目的是向 RepaintManager 提出请求,后者的职责是决定如何最好地管理实际的绘制过程,包括将实际绘制事件的数量压缩到尽可能少的数量. repaint 也应该尽可能快地运行,因为期望该方法在短时间内连续调用很多次是合理的。绘画也可能由于不会触发repaint 的其他原因而发生,因此您会错过这些事件。
    猜你喜欢
    • 1970-01-01
    • 2019-04-17
    • 2019-12-08
    • 2013-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-29
    • 2015-10-18
    相关资源
    最近更新 更多