简体   繁体   中英

JPanel gets repainted several times on resize, sometimes not at all

First, some background. I'm building my first GUI app in Java, and I'm not sure if I'm going about it the right way as I'm not familiar with the GUI classes. The end goal is to have a resizeable, zoomable, scrollable map built from Open Street Map data.

What I have right now is a subclass of JPanel I call LinePanel inside a JFrame, and I use the Graphics object to draw lines representing roads. This worked fine for the purpose of checking to see if I'm parsing and interpreting the data correctly, but it seems insufficient and naive. I'm already having an issue where the JPanel gets repainted several times on a resize, causing the map to bug out to the point where my application would need an epilepsy warning.

This is the code I have now:

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

I'm calling repaint on resize because It doesn't do that automatically, another sign that I'm doing something wrong. I also clear the JPanel manually with g.fillRect because calling super.repaint() before redrawing the map causes nothing to appear...

Essentially, I'd just like some guidance from more senior Java programmers as to how I should go about this whole endeavor. If I'm on the right track, feel free to just nudge me in the right direction instead of setting me on a new path, but I doubt that this is the case.

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

This is not how painting works in Swing. See Performing Custom Painting and Painting in AWT and Swing for more details about how painting should be done in Swing

You should start by getting rid of your repaint method and replace it with a paintComponent method...

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

Yes, a repaint event might be generated a number of times when a component is re-sized, but repaint events can also be reduced by the RepaintManager automatically (ie the RepaintManager might be called 100 times to repaint a component, might only produce 10 actual repaint events - as an example)...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM