GridBagLayout 未在图像加载时调整大小

GridBagLayout not resizing on image load

我有一个 JFrame。它使用 JPanel 作为其内容窗格,而 JPanel 使用 GridBagLayout 作为其 LayoutManager。该 JPanel 包含另外两个项目:一个按钮和另一个 JPanel。在程序启动时,使用 ImageIO.read(...) 将图像作为 BufferedImage 从文件加载到最低级别的 JPanel 中。在这里,一切都变得支离破碎。

图像正确加载,我可以在屏幕上看到它的一个小角(调试器中指定的 14 像素正方形)。我无法弄清楚会导致布局增长并适合屏幕上最低级别 JPanel 中的整个图像。调试器中的图像显示正确大小为 500 像素。 CardImagePanel 的首选大小正确显示为与图像大小相同。但是布局不会遵循首选大小,除非我使用 setSize(...) 手动设置 CardImagePanel 大小,我很确定这对于 GBL 来说不是必需的。

我已经尝试在整个程序中对每个 JFrame、JPanel、布局、网格包、图像等调用 revalidate() 和 repaint(),只是找不到调用它们的正确位置或时间让这个东西工作。目前我一直在尝试让图像加载不正确并使用按钮强制重新验证和重绘,但即使是这个显式调用也没有做任何事情。

我快疯了,我会做任何事情来让这东西正常工作。

这是我对整个愚蠢的事情的所有代码(减去导入和包规范。

P1s1.java:

public class P1s1 {

    public static void main(String[] args) {
        // TODO code application logic here
        build();
    }

    public static void build()
    {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(new Dimension(640, 480));
        frame.setContentPane(new GuiPanel(frame));
        frame.setVisible(true);
    }
}

GuiPanel.java:

public class GuiPanel extends JPanel {

    JFrame parentFrame;
    JButton imageLoaderButton;
    CardImagePanel cardImagePanel;
    LayoutManager layout;
    GridBagLayout gridBagLayout;
    GridBagConstraints constraints;

    public GuiPanel(JFrame frame)
    {
        parentFrame = frame;

        constraints = new GridBagConstraints();
        gridBagLayout = new GridBagLayout();

        layout = gridBagLayout;
        this.setLayout(layout);

        this.setBorder(BorderFactory.createLineBorder(Color.black));

        setupImageLoaderButton(imageLoaderButton);

        cardImagePanel = new CardImagePanel();
        this.add(cardImagePanel);
    }

    private void setupImageLoaderButton(JButton button)
    {
        button = new JButton("Click to load image!");
        ActionListener imageLoaderListener;
        imageLoaderListener = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                System.out.println("Button clicked.");

                cardImagePanel.revalidate();
                cardImagePanel.repaint();

                GuiPanel.this.revalidate();
                GuiPanel.this.repaint();

                parentFrame.revalidate();
                parentFrame.repaint();
            }
        };
        button.addActionListener(imageLoaderListener);
        this.add(button);
    }
}

CardImagePanel.java:

public class CardImagePanel extends JPanel {

    BufferedImage cardImage;

    public CardImagePanel()
    {
        this.setBorder(BorderFactory.createLineBorder(Color.black));

        try {
            cardImage = ImageIO.read(new File("c:\dev\cards\2_of_clubs.png"));
            this.setPreferredSize(new Dimension(cardImage.getWidth(), cardImage.getHeight()));
        } catch (IOException ex) {
            System.out.println("Exception trying to load image file.");
        }
    }

    // The getPreferredSize() override was suggested by MadProgrammer.
    // It did not solve the issue, but see MadProgrammer's updated,
    // accepted answer below for the correct solution.  The rest of the
    // code reflects my original attempt to solve the issue.
    @Override
    public Dimension getPreferredSize()
    {
        return cardImage != null ? new Dimension(cardImage.getWidth(), cardImage.getHeight()) : super.getPreferredImage();
    }

    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);

        g.drawImage(cardImage, 0, 0, this);
    }
}

您的 CardImagePanel 没有首选尺寸,因此布局管理器不知道如何正确处理尺寸。

几个解决方案:

  1. 无需创建自定义 class 即可显示图像。只需使用 JLabel 来显示图像。 JLabel 将 return 图像的首选大小。
  2. 如果您确实使用了 CardImagePane,那么您需要将 CardImagePanel 的 getPreferredsize() 方法重写为 return 图像的大小。

GridBagLayout 依赖于一个组件,告诉它它想要的大小(以及它在缓解时的最小和最大大小)。您需要覆盖 CardImagePanelgetPreferredSize 方法,返回您希望组件的大小

public class CardImagePanel extends JPanel {

    BufferedImage cardImage;

    public CardImagePanel() {
        this.setBorder(BorderFactory.createLineBorder(Color.black));

        try {
            cardImage = ImageIO.read(new File("c:\dev\cards\2_of_clubs.png"));
        } catch (IOException ex) {
            System.out.println("Exception trying to load image file.");
        }
    }

    @Override
    public Dimension getPreferredSize() {
        return cardImage != null ? new Dimension(cardImage.getWidth(), cardImage.getHeight()) : super.getPreferredSize();
    }

    @Override
    public Dimension getMinimumSize() {
        return getPreferredSize();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        g.drawImage(cardImage, 0, 0, this);
    }
}

查看How to Use GridBagLayout了解更多详情