JPanel在重新调整大小时会多次重新绘制,有时根本不会重新绘制

时间:2015-03-19 02:46:59

标签: java swing jpanel awt

首先,一些背景知识。我正在用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;
                        }
                    }
                }
            }
        }
    }
}

我在调整大小时调用重绘,因为它没有自动执行此操作,这是我做错事的另一个迹象。我还使用g.fillRect手动清除JPanel,因为在重绘地图之前调用super.repaint()会导致无法显示...

基本上,我只是喜欢来自更高级Java程序员的一些指导,关于我应该如何进行这一整个努力。如果我走在正确的轨道上,请随意向我推进正确的方向,而不是让我走上新的道路,但我怀疑是这样的。

1 个答案:

答案 0 :(得分:2)

  • public void repaint() { NO,NO,NO
  • Graphics2D g = (Graphics2D) this.getGraphics(); - NO,NO,NO

这不是绘画在Swing中的作用。有关如何在Swing

中完成绘画的详细信息,请参阅Performing Custom PaintingPainting in AWT and 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个实际的重绘事件 - 作为一个例子)......