JPanel背景图像不适用于其中的JPanel

4
我正在处理一个简单的注册窗口,当Java应用程序打开时会出现。
它是一个JFrame,其中包含一个JPanel,该JPanel具有文本字段、标签和另一个面板,该面板还包含文本字段和标签。我的问题是外部面板有一个背景图片,但它不适用于内部面板,如下图所示:
https://i.imgur.com/MTgNO3p.png
以下是整个窗口代码:
public void start() {

    try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    frame = new JFrame("Chat");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    //frame.setMaximumSize(new Dimension((int)screenSize.getWidth()-1000, (int)screenSize.getHeight()-1000));
    frame.setMinimumSize(new Dimension((int)screenSize.getWidth()/2-200,(int) screenSize.getHeight()/2));
    frame.setResizable(false);

        welcome = new LoginPanel();
        welcome.setLayout(new BoxLayout(welcome, BoxLayout.Y_AXIS));
        welcome.setBorder(BorderFactory.createEmptyBorder(50, welcome.getWidth()/2-500, 50, welcome.getWidth()/2 -500));

        //repaintThread = new Thread(new RepaintRunnable(frame, welcome));
        //repaintThread.start();


            request = new JLabel("Please fill the required fields below:");
            request.setFont(new Font("Serif", Font.BOLD, 20));
            request.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
            request.setAlignmentX(Component.CENTER_ALIGNMENT);

            userInfo = new JPanel();
            userInfo.setLayout(new BoxLayout(userInfo, BoxLayout.Y_AXIS));
            userInfo.setAlignmentX(Component.CENTER_ALIGNMENT);

                Font fieldType = new Font("Serif", Font.PLAIN, 15);

                PlainDocument doc = new PlainDocument();
                doc.setDocumentFilter(new NameDocument());

                enterFirstName = new JLabel("First name:");
                enterFirstName.setAlignmentX(Component.LEFT_ALIGNMENT);
                enterFirstName.setFont(fieldType);
                firstName = new JTextField(20);
                firstName.setMaximumSize(firstName.getPreferredSize());
                firstName.setDocument(NameDocument.getNewNameDocument(NameDocument.NO_SPACE));
                firstName.getDocument().addDocumentListener(new ChangeDocumentListener());
                firstName.addActionListener(new ConfirmListener());
                firstName.setAlignmentX(Component.LEFT_ALIGNMENT);

                enterSecName= new JLabel("Surname:");
                enterSecName.setAlignmentX(Component.LEFT_ALIGNMENT);
                enterSecName.setFont(fieldType);
                secName = new JTextField(30);
                secName.setMaximumSize(secName.getPreferredSize());
                secName.setDocument(NameDocument.getNewNameDocument(NameDocument.HAS_SPACE));
                secName.getDocument().addDocumentListener(new ChangeDocumentListener());
                secName.addActionListener(new ConfirmListener());
                secName.setAlignmentX(Component.LEFT_ALIGNMENT);

                enterNickname = new JLabel("Nickname (how other people will see you in chat):");
                enterNickname.setAlignmentX(Component.LEFT_ALIGNMENT);
                enterNickname.setFont(fieldType);
                nickname = new JTextField(30);
                nickname.setMaximumSize(nickname.getPreferredSize());
                nickname.setDocument(NameDocument.getNewNameDocument(NameDocument.NO_SPACE));
                nickname.addActionListener(new ConfirmListener());
                nickname.setAlignmentX(Component.LEFT_ALIGNMENT);

            userInfo.add(enterFirstName);
            userInfo.add(firstName);
            userInfo.add(enterSecName);
            userInfo.add(secName);
            userInfo.add(enterNickname);
            userInfo.add(nickname);

            confirm = new JButton("Submit");
            confirm.setAlignmentX(Component.CENTER_ALIGNMENT);
            confirm.setEnabled(false);
            confirm.addActionListener(new ConfirmListener());

        welcome.add(request);
        welcome.add(userInfo);
        welcome.add(new Box.Filler(new Dimension(10, 10), new Dimension(10, 10), new Dimension(10, 10)));
        welcome.add(confirm);

    frame.getContentPane().add(BorderLayout.CENTER, welcome);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
}

这里是LoginPanel代码(外部JPanel):

public class LoginPanel extends JPanel {
public void paintComponent(Graphics g) {
    try {
        super.paintComponent(g);
        BufferedImage background = ImageIO.read(new File("Background.jpg"));
        g.drawImage(background, 0, 0, getWidth(), getHeight(), null);
    } catch(Exception ex) {
        ex.printStackTrace();
    }
}
}

如果有人能给我一些建议,让这段代码更好,因为我是Java的初学者,那将会很棒。


2
  1. frame.setMinimumSize(new Dimension((int)screenSize.getWidth()/2-200,(int) screenSize.getHeight()/2)); 这是基于所需大小的猜测。在添加所有组件后,我会改为调用 frame.pack(); 然后立即调用 frame.setMinimumSize(frame.getSize());
  2. new Font("Serif", Font.PLAIN, 15); 最好改为 new Font(Font.SERIF, Font.PLAIN, 15); 以进行编译时检查。
  3. g.drawImage(background, 0, 0, getWidth(), getHeight(), null); 最好改为 g.drawImage(background, 0, 0, getWidth(), getHeight(), this);(使用 ImageObserver)。
- Andrew Thompson
@AndrewThompson 谢谢,我真的需要这样的建议更频繁。这是我第一次向一个懂Java的人展示它,所以再次感谢。 - Ido Fang Bentov
2个回答

5

记得在任何遮盖图片显示的JPanel(以及一些其他组件-虽然不是全部)上调用setOpaque(false);。这将允许背景图片透过显示。对于JLabel,您不必这样做,因为它们默认情况下是透明的(非不透明),但您需要对JPanels进行此操作。

因此,您需要:

userInfo = new JPanel();
userInfo.setOpaque(false);

另一个问题是,你永远不应该在paintComponent方法内部读取图像。这个方法可能会被频繁调用,如果可以并且应该只读取一次,那么为什么要重新读取图片呢。更重要的是,这个方法应该尽可能地快,因为不必要地减慢它的速度会减慢程序的响应速度。将图像读入一次,并将其存储在变量中,然后在paintComponent中显示。

例如:

public class LoginPanel extends JPanel {
    private BufferedImage background;

    public LoginPanel(BufferedImage background) {
        this.background = background;
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (background != null) {
            g.drawImage(background, 0, 0, getWidth(), getHeight(), this);
        }
    }
}

读取图片,然后将其传递到您的LoginPanel类的构造函数中。

1
@AndrewThompson:没错,没错。提醒我一下,不使用 null 在这里有帮助吗?编辑:算了,我看到你对 OP 的评论了。 - Hovercraft Full Of Eels
关于drawImage中的null/this建议,针对OP进行更详细的说明。图片可能会异步加载(在代码语句返回后)。如果您将正在加载的图像提供给实现ImageObserver的组件,则随着更多的图像加载,事件将被发送到图像观察器,从而导致调用repaint()。但是,在自定义绘制时,我们需要显式地将图像观察器(组件)添加到drawImage(..)方法调用中。 - Andrew Thompson
非常感谢,您还回答了我的另一个问题,关于如何使paintComponent()方法运行更快,因为当我调整窗口大小时,背景图像也需要一些时间才能调整大小。 - Ido Fang Bentov

3

虽然和您的问题无关,但是:

frame.getContentPane().add(BorderLayout.CENTER, welcome);

自从JDK 4之后,您不需要使用 getContentPane() 方法,只需使用 frame.add(...) 即可将组件添加到内容面板中。
此外,您正在使用错误的 add(...) 方法。 您正在使用 add(constraint, component)。 如果您阅读该方法的API,则会告识您使用add(component, constraint)方法。
因此,您可以使用以下方式:
frame.add(welcome, BorderLayout.CENTER);

我仍然喜欢通过 getContentPane() 添加组件的“旧方法”,因为它让我想起我真正添加组件的位置,并且在向人们解释为什么他们尝试在 JFrame 上使用 BoxLayout 时出现问题时有所帮助(必须将 contentPane 传递到构造函数中,而不是 JFrame)。但你已经知道这一点了。+1(当然)感谢您的深入见解! - Hovercraft Full Of Eels
@HovercraftFullOfEels,当向人们解释为什么他们尝试在JFrame上使用BoxLayout时无法正确工作时(您必须将contentPane传递到构造函数中),这很有帮助-说得好。 - camickr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接