【问题标题】:GridBagLayout not resizing on image loadGridBagLayout 未在图像加载时调整大小
【发布时间】:2015-09-21 05:15:03
【问题描述】:

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

图像加载正确,我可以在屏幕上看到它的一个小角(调试器中指定的 14px 正方形)。我无法弄清楚会导致布局增长并将整个图像放在屏幕上最低级别的 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);
    }
}

【问题讨论】:

    标签: java swing layout-manager bufferedimage gridbaglayout


    【解决方案1】:

    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了解更多详情

    【讨论】:

    • 我试过你的建议,但没有效果。正如我的帖子中所解释的,首选尺寸已经返回了预期的尺寸。我会花一些时间阅读您的链接文章以获取更多提示。
    • 您的代码对我来说可以正常工作,如果图像对于容器的可用空间来说太大时会发生什么,这意味着它将求助于它的最小尺寸,这可能会引起关注。我还建议将 frame.setSize(new Dimension(640, 480)); 更改为 frame.pack()
    • 在这种情况下手动将最小尺寸设置为图像尺寸是否被认为是一种好习惯?编辑:抱歉,刚刚看到您的代码更新。这看起来很有希望。
    • 如果您的意思是调用setMinimumSize,那么不,请参阅Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing? 了解更多详情。一般来说,最好根据您的需求和期望覆盖这些方法
    【解决方案2】:

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

    几个解决方案:

    1. 无需创建自定义类来显示图像。只需使用 JLabel 来显示图像。 JLabel 将返回图像的首选大小。
    2. 如果您使用 CardImagePane,则需要覆盖 CardImagePanel 的 getPreferredsize() 方法以返回图像的大小。

    【讨论】:

    • JLabel 肯定更容易
    • 手动设置JPanel的首选大小实际上并没有设置它?当我在加载图像后在调试器中查看 JPanel 的首选尺寸时,我确实看到它报告了我期望的尺寸的首选尺寸,但我还是会尝试这个解决方案,看看它是否有帮助。
    • 我应该补充说这是一个学校项目。这是一个项目约束,我们扩展 JPanel 以创建类似于 CardImagePanel 的东西来容纳我们的图像。至于第2项,我试过了,没有效果。我会更新我的帖子以反映这一变化。
    • @fts_acer, As for item 2, I tried this and it had no effect. - 那么你的代码是错误的。发布您的代码,显示您的尝试。
    • @camickr 标记为已接受的答案解决了该问题。我帖子中的代码已经更新,以反映我尝试的更新。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-29
    • 2020-03-23
    • 2012-03-20
    • 2014-05-26
    • 1970-01-01
    相关资源
    最近更新 更多