Components disappear after resizing JPanel

2020-02-12 14:03发布

I am trying to create JPanel with draggable crosses which appear after mouse clicking. Everything works fine but when I resize the JPanel the crosses disappear. I tried to override the paintComponent method in my JPanel but then all crosses are at coordinates (0,0). How can I fix it?

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class CrossPanel extends JPanel implements MouseListener {

private int orderOfCross = 0;
private ArrayList<Cross> crosses;
private int defaultSizeOfCrosses = 10;

CrossPanel() {
    setOpaque(false);
    addMouseListener(this);
    crosses = new ArrayList<Cross>();
}

@Override
public void mouseClicked(MouseEvent e) {
    int x = e.getX();
    int y = e.getY();
    Cross cross = new Cross(orderOfCross++, defaultSizeOfCrosses);
    crosses.add(cross);
    cross.setLocation(x - defaultSizeOfCrosses, y - defaultSizeOfCrosses);
    add(cross);
    repaint();
}

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    //        for (int i = 0; i < crosses.size(); i++) {
    //            crosses.get(i).paint(g);
    //        }
}

@Override
public void mousePressed(MouseEvent e) {}

@Override
public void mouseReleased(MouseEvent e) {}

@Override
public void mouseEntered(MouseEvent e) {}

@Override
public void mouseExited(MouseEvent e) {}

public static void main(String[] args) {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    CrossPanel crossPane = new CrossPanel();
    f.getContentPane().add(crossPane);
    f.setSize(600, 500);
    f.setLocation(200, 200);
    f.setVisible(true);
}
}

class Cross extends JComponent {

private int order;
protected Cursor draggingCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
private volatile int draggedAtX, draggedAtY;
int size;

public Cross(int order, int size) {
    this.order = order;
    this.size = size;
    this.setBounds(0, 0, 4 * size, 3 * size + 10);
    addDragListeners();
    setCursor(draggingCursor);
}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    g2.setColor(Color.red);
    g2.setStroke(new BasicStroke(3));
    g2.drawLine(0, size, size + size, size);
    g2.drawLine(size, 0, size, size + size);
    Font f = new Font("Monospaced", Font.BOLD, size + 10);
    g2.setFont(f);
    g2.drawString(String.valueOf(order), size - size / 2, 3 * size + 10);
}

private void addDragListeners() {
    addMouseListener(new MouseAdapter() {
        @Override
        public void mousePressed(MouseEvent e) {
            draggedAtX = e.getX();
            draggedAtY = e.getY();
        }
    });
  addMouseMotionListener(new MouseMotionAdapter() {
        @Override
        public void mouseDragged(MouseEvent e) {
            Point newLocation = new Point(e.getX() - draggedAtX + getLocation().x,  e.getY() - draggedAtY + getLocation().y);
            setLocation(newLocation);
        }
    });
}
}

1条回答
看我几分像从前
2楼-- · 2020-02-12 14:52

I very rarely see a use for a null layout, for all the perceived benefits, there are simply to many draw backs.

The entire Swing API has been designed around the use of layout managers so you'd be crazy (IMHO) to simply throw all that work away.

If you find yourself in a position where the available layout managers don't seem to do what you want, it might be more worth while to write you own.

Here, I've presented a PropertionalLayoutManager whose intention is to provide layout capabilities that will place components on a container based an percentage of the width/height of the parent component. This means, as the parent component is resized, the child components will reposition themselves at a percentage of the parent size.

enter image description hereenter image description here

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LayoutManager2;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class CrossPanel extends JPanel implements MouseListener {

    private int orderOfCross = 0;
    private ArrayList<Cross> crosses;
    private int defaultSizeOfCrosses = 10;

    CrossPanel() {
        setOpaque(false);
        addMouseListener(this);
        crosses = new ArrayList<Cross>();
        setLayout(new PropertionalLayoutManager());
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        int x = e.getX();
        int y = e.getY();
        Cross cross = new Cross(orderOfCross++, defaultSizeOfCrosses);

        float xPos = (float)x / (float)getWidth();
        float yPos = (float)y / (float)getHeight();

        crosses.add(cross);

        add(cross, new PropertionalConstraints(xPos, yPos));
        revalidate();
    }

    public static String format(float value) {
        return NumberFormat.getNumberInstance().format(value);
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }

    public static void main(String[] args) {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        CrossPanel crossPane = new CrossPanel();
        f.getContentPane().add(crossPane);
        f.setSize(600, 500);
        f.setLocation(200, 200);
        f.setVisible(true);
    }

    public class Cross extends JComponent {

        private int order;
        protected Cursor draggingCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
        private volatile int draggedAtX, draggedAtY;
        int size;

        public Cross(int order, int size) {
            this.order = order;
            this.size = size;
//            this.setBounds(0, 0, 4 * size, 3 * size + 10);
            addDragListeners();
            setCursor(draggingCursor);
            Font f = new Font("Monospaced", Font.BOLD, size + 10);
            setFont(f);

        }

        @Override
        public Dimension getPreferredSize() {
            // This is dangrous, you are making assumptions about platforms
            // that you have no eviednce to support.
            FontMetrics fm = getFontMetrics(getFont());
            return new Dimension(Math.max(fm.stringWidth(String.valueOf(order)), size), size + fm.getHeight());
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            g2.setColor(Color.red);
            g2.setStroke(new BasicStroke(3));

            FontMetrics fm = g2.getFontMetrics();

            int width = getWidth() - 1;
            int height = getHeight() - 1;

            int x = (width - fm.stringWidth(String.valueOf(order))) / 2;
            int y = fm.getAscent();
            g2.drawString(String.valueOf(order), x, y);

            int crossSize = Math.min(width, height - fm.getHeight());
            x = (width - crossSize) / 2;
            y = fm.getHeight();
            g2.drawLine(x, y, x + crossSize, y + crossSize);
            g2.drawLine(x + crossSize, y, x, y + crossSize);
        }

        private void addDragListeners() {
            addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    draggedAtX = e.getX();
                    draggedAtY = e.getY();
                }

            });
            addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseDragged(MouseEvent e) {
                    Point newLocation = new Point(e.getX() - draggedAtX + getLocation().x, e.getY() - draggedAtY + getLocation().y);
                    setLocation(newLocation);
                }

            });
        }

    }

    public class PropertionalConstraints {

        private float x;
        private float y;

        public PropertionalConstraints(float x, float y) {
            this.x = x;
            this.y = y;
        }

        public float getX() {
            return x;
        }

        public float getY() {
            return y;
        }

        public void setX(float x) {
            if (x > 1f) {
                x = 1f;
            } else if (x < -0f) {
                x = 0f;
            }
            this.x = x;
        }

        public void setY(float y) {
            if (y > 1f) {
                y = 1f;
            } else if (y < -0f) {
                y = 0f;
            }
            this.y = y;
        }

    }

    public class PropertionalLayoutManager implements LayoutManager2 {

        private Map<Component, PropertionalConstraints> mapConstraints;

        public PropertionalLayoutManager() {
            mapConstraints = new HashMap<>(25);
        }

        public PropertionalConstraints getConstraintsFor(Component comp) {
            return mapConstraints.get(comp);
        }

        public void setConstraintsFor(Component comp, PropertionalConstraints pc) {
            mapConstraints.put(comp, pc);
        }

        @Override
        public void addLayoutComponent(Component comp, Object constraints) {
            if (constraints instanceof PropertionalConstraints) {
                mapConstraints.put(comp, (PropertionalConstraints) constraints);
            } else {
                throw new IllegalArgumentException("Constraints must be PropertionalConstraints");
            }
        }

        @Override
        public Dimension maximumLayoutSize(Container target) {
            return preferredLayoutSize(target);
        }

        @Override
        public float getLayoutAlignmentX(Container target) {
            return 0.5f;
        }

        @Override
        public float getLayoutAlignmentY(Container target) {
            return 0.5f;
        }

        @Override
        public void invalidateLayout(Container target) {

        }

        @Override
        public void addLayoutComponent(String name, Component comp) {

        }

        @Override
        public void removeLayoutComponent(Component comp) {
            mapConstraints.remove(comp);
        }

        @Override
        public Dimension preferredLayoutSize(Container parent) {
            return parent.getSize();
        }

        @Override
        public Dimension minimumLayoutSize(Container parent) {
            return preferredLayoutSize(parent);
        }

        @Override
        public void layoutContainer(Container parent) {
            int width = parent.getWidth();
            int height = parent.getHeight();
            for (Component comp : parent.getComponents()) {
                PropertionalConstraints con = mapConstraints.get(comp);
                if (con != null) {
                    int x = (int)(width * con.getX());
                    int y = (int)(height * con.getY());
                    comp.setSize(comp.getPreferredSize());
                    comp.setLocation(x, y);
                } else {
                    comp.setBounds(0, 0, 0, 0);
                }
            }
        }

    }

}

On some side notes, you are using "magic" numbers to determine the size and rendering position of certain elements. This is a very bad idea. You should, especially when painting or printing, base all these values on empirical values.

In this example, I've reverted to using FontMertrics to provide the required information to more accurately calculate the size and positions of various elements. This will allow for better cross platform support, as not all fonts are rendered the same across all platforms ;)

查看更多
登录 后发表回答