【问题标题】:Components disappear after resizing JPanel调整 JPanel 大小后组件消失
【发布时间】:2013-04-05 21:40:55
【问题描述】:

我正在尝试使用鼠标单击后出现的可拖动十字创建 JPanel。一切正常,但是当我调整 JPanel 的大小时,十字架消失了。我试图在我的 JPanel 中覆盖paintComponent 方法,但是所有的十字都在坐标(0,0)。我该如何解决?

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

【问题讨论】:

    标签: java swing jpanel paintcomponent


    【解决方案1】:

    我很少看到null 布局的用途,对于所有可感知的好处,有很多缺点。

    整个 Swing API 都是围绕布局管理器的使用而设计的,所以你会疯了(恕我直言)简单地扔掉所有这些工作。

    如果您发现自己处于可用的布局管理器似乎无法满足您的需求,那么编写您自己的布局管理器可能更值得。

    在这里,我提出了一个PropertionalLayoutManager,其目的是提供布局功能,根据父组件的宽度/高度百分比将组件放置在容器上。这意味着,当父组件调整大小时,子组件将按照父组件的一定百分比重新定位。

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

    在某些方面,您正在使用“魔术”数字来确定某些元素的大小和渲染位置。这是一个非常糟糕的主意。您应该,尤其是在绘画或印刷时,将所有这些值都基于经验值。

    在此示例中,我已恢复为使用 FontMertrics 来提供所需的信息,以便更准确地计算各种元素的大小和位置。这将允许更好的跨平台支持,因为并非所有字体在所有平台上都呈现相同;)

    【讨论】:

    • 正是我需要的。感谢您的改进。
    • +1 用于制作您自己的LayoutManager。您是否为这个答案做了所有这些工作?
    • @syb0rg 是的,这就是我不喜欢空布局的原因;)
    猜你喜欢
    • 1970-01-01
    • 2019-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多