为什么仅在我在 Swing 中调整 window 的大小后才显示 JFrame 的背景图像?

Why is a Background Image of a JFrame only showing after i resize the window in Swing?

我注意到将图像设置为 JFrames 的背景时出现了一些奇怪的行为。我希望在创建两个框架时都显示背景图像。 当我在我的主要方法中创建 Window1 时,它只在我手动调整 window 的大小后显示背景。当我单击 Window1 中的按钮并在 Window1 中创建 window2 时,Window2 会正确显示背景图像。当我在我的主要方法中创建 Window2 时,背景图像也无法正确显示。

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class TestStart {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Window1();
            }
        });
    }
}

public class Window1 extends JFrame {

    Image img = Toolkit.getDefaultToolkit().getImage("C:\Users\Tim\Desktop\water.jpg");

    public Window1() {
        super("gridtest");

        setSize(600, 500);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());

        this.setContentPane(new JPanel(){
            public void paintComponent(Graphics g){
                super.paintComponent(g);
                g.drawImage(img,0,0,getWidth(),getHeight(),null);
            }
        });

        JButton btn = new JButton("klick");
        add(btn, BorderLayout.CENTER);

        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                new Window2();
            }
        });
        pack();
        setVisible(true);
    }
}

public class Window2 extends JFrame {

    Image img = Toolkit.getDefaultToolkit().getImage("C:\Users\Tim\Desktop\water.jpg");

    public Window2() {
        super("gridsecond");

        setSize(600, 500);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());

        this.setContentPane(new JPanel(){
            public void paintComponent(Graphics g){
                super.paintComponent(g);
                g.drawImage(img,0,0,getWidth(),getHeight(),null);
            }
        });
        pack();
        setVisible(true);
    }
}

我尝试在我的代码中的不同点添加 revalidate()/repaint() 调用并调用 paint 方法,但除了我上面描述的奇怪行为外,我无法在不调整我的大小的情况下显示背景图像window.

免责声明:我知道为每个 window 创建一个新的 JFrame 不是好的做法,但我无法更改整个项目结构,所以我坚持这样做。

那么,一系列问题可能会导致您的问题...

首先,使用Toolkit.getDefaultToolkit().getImage

Image img = Toolkit.getDefaultToolkit().getImage("C:\Users\Tim\Desktop\water.jpg");

问题在于,图像加载是在一个单独的线程上进行的。这意味着当 getImage returns 图像可能已实际加载也可能未加载

其次,当调用 drawImage 时,您将 null 作为 ImageConsumer 传递,因此组件不会收到图像 "loaded" 状态和将无法相应地安排组件更新...

this.setContentPane(new JPanel(){
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        g.drawImage(img,0,0,getWidth(),getHeight(),null);
    }
});

三,这更多是副作用,但是,您已经使用 JPanel 手动设置了 contentPane,默认情况下使用 FlowLayout,因此使用 BorderLayout 约束实际上有点毫无意义。

setLayout(new BorderLayout());

this.setContentPane(new JPanel(){
//...
JButton btn = new JButton("klick");
add(btn, BorderLayout.CENTER);

此外,BorderLayout 是基于 window 的组件使用的默认布局管理器 ;)

那么,您将如何解决这个问题?那么,您可以立即将 this 作为 ImageConsumer...

g.drawImage(img,0,0,getWidth(),getHeight(),this);

这将允许组件监视图像的加载状态并根据需要触发重绘。

一个长期的解决方案是停止使用 Toolkit.getDefaultToolkit().getImage(并推而广之,ImageIcon),因为它们可能很烦人,并开始使用 ImageIO

除了支持更广泛的图像格式外,API 不会 return 直到图像完全加载或发生错误(尝试诊断其他加载问题 APIs )

有关详细信息,请参阅 Reading/Loading images