为我的菜单设置背景图像并可调整大小

Setting up a background image for my menu and being resizable

所以我正在尝试为我正在开发的游戏制作菜单。

我想在我的 menuPanel 中放置一张图片作为背景,但我不知道如何在每次提升 window 时让图片重新缩放。我制作了一个 JLabel 并从我的主要方法导入了一个图像,当我启动游戏时我可以看到图像已正确导入,但我想填满所有 menuPanel 并拉伸为我将 window 提高到全屏或减小到我的框架的最小尺寸。 我该怎么做?

正如您在屏幕截图中看到的,我希望文本位于图像之上,图像作为背景和全屏。

public class Window extends Canvas{

    private static final long serialVersionUID = 6331412385749386309L;
    
    private static final int WIDTH = 1024, HEIGHT = WIDTH / 16 * 9;
    private JFrame frame;
    
    private JPanel mainPanel;
    private JPanel menuPanel;
    private JPanel buttonsPanel;
    private JPanel playPanel;
    private JPanel optionsPanel;
    
    private JButton playBtn;
    private JButton optionsBtn;
    private JButton quitBtn;
    
    private int currWidth = WIDTH, currHeight = HEIGHT;

    public Window(String title, Game game) {
        frame = new JFrame(title);
        
        frame.setSize(1024, 576);
        frame.setMinimumSize(new Dimension(WIDTH, HEIGHT));
        
        frame.requestFocus();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(true);
        frame.setLocationRelativeTo(null);
        menu();
        game.start();
    }
    
    private void menu() {
        frame.getContentPane().setLayout(new BorderLayout(0, 0));
        mainPanel = new JPanel();
        mainPanel.setBackground(new Color(255, 255, 255));
        frame.getContentPane().add(mainPanel);
        mainPanel.setLayout(new CardLayout(0, 0));
        
        // menuPanel config
        menuPanel = new JPanel();
        menuPanel.setForeground(new Color(0, 0, 0));
        menuPanel.setBackground(new Color(0, 0, 0));
        mainPanel.add(menuPanel, "menuPanel");
        
        buttonsPanel = new JPanel();
        buttonsPanel.setBorder(null);
        buttonsPanel.setBackground(new Color(0, 0, 0));
        
            // playBtn config
        playBtn = new JButton("Play");
        playBtn.setForeground(new Color(255, 255, 255));
        playBtn.setFont(new Font("Segoe Script", Font.BOLD, 40));
        playBtn.setOpaque(false);
        playBtn.setContentAreaFilled(false);
        playBtn.setBorderPainted(false);
        playBtn.setFocusPainted(false);
        
            // optionsBtn config
        optionsBtn = new JButton("Options");
        optionsBtn.setForeground(new Color(255, 255, 255));
        optionsBtn.setFont(new Font("Segoe Script", Font.BOLD, 35));
        optionsBtn.setOpaque(false);
        optionsBtn.setContentAreaFilled(false);
        optionsBtn.setBorderPainted(false);
        optionsBtn.setFocusPainted(false);
        
            //quitBtn config
        quitBtn = new JButton("Quit");
        quitBtn.setForeground(new Color(255, 255, 255));
        quitBtn.setFont(new Font("Segoe Script", Font.BOLD, 35));
        quitBtn.setOpaque(false);
        quitBtn.setContentAreaFilled(false);
        quitBtn.setBorderPainted(false);
        quitBtn.setFocusPainted(false);
        
        GroupLayout gl_buttonsPanel = new GroupLayout(buttonsPanel);
        gl_buttonsPanel.setHorizontalGroup(
            gl_buttonsPanel.createParallelGroup(Alignment.TRAILING)
                .addGroup(gl_buttonsPanel.createSequentialGroup()
                    .addContainerGap()
                    .addGroup(gl_buttonsPanel.createParallelGroup(Alignment.LEADING)
                        .addComponent(quitBtn, GroupLayout.DEFAULT_SIZE, 175, Short.MAX_VALUE)
                        .addComponent(playBtn, GroupLayout.DEFAULT_SIZE, 175, Short.MAX_VALUE)
                        .addComponent(optionsBtn, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                    .addContainerGap())
        );
        gl_buttonsPanel.setVerticalGroup(
            gl_buttonsPanel.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_buttonsPanel.createSequentialGroup()
                    .addContainerGap()
                    .addComponent(playBtn)
                    .addPreferredGap(ComponentPlacement.RELATED)
                    .addComponent(optionsBtn, GroupLayout.PREFERRED_SIZE, 74, GroupLayout.PREFERRED_SIZE)
                    .addPreferredGap(ComponentPlacement.RELATED)
                    .addComponent(quitBtn, GroupLayout.PREFERRED_SIZE, 71, GroupLayout.PREFERRED_SIZE)
                    .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        buttonsPanel.setLayout(gl_buttonsPanel);
        
        //
        JLabel menuImageLabel = new JLabel(new ImageIcon(Game.menu_image.getScaledInstance(700, 400, Image.SCALE_FAST)));
        //
        
        GroupLayout gl_menuPanel = new GroupLayout(menuPanel);
        gl_menuPanel.setHorizontalGroup(
            gl_menuPanel.createParallelGroup(Alignment.TRAILING)
                .addGroup(gl_menuPanel.createSequentialGroup()
                    .addComponent(menuImageLabel, GroupLayout.PREFERRED_SIZE, 762, GroupLayout.PREFERRED_SIZE)
                    .addGap(0)
                    .addComponent(buttonsPanel, GroupLayout.DEFAULT_SIZE, 195, Short.MAX_VALUE))
        );
        gl_menuPanel.setVerticalGroup(
            gl_menuPanel.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_menuPanel.createSequentialGroup()
                    .addGap(161)
                    .addComponent(buttonsPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addGap(124))
                .addComponent(menuImageLabel, GroupLayout.DEFAULT_SIZE, 537, Short.MAX_VALUE)
        );
        menuPanel.setLayout(gl_menuPanel);
        
        // playPanel config
        playPanel = new JPanel();
        playPanel.setBackground(new Color(0, 0, 255));
        mainPanel.add(playPanel, "playPanel");
        
        // optionsPanel config
        optionsPanel = new JPanel();
        optionsPanel.setBackground(new Color(255, 0, 0));
        mainPanel.add(optionsPanel, "optionsPanel");
        
        frame.setVisible(true);
        
        setActions();
    }
    
    private void setActions() {
        
        // playBtn action
        playBtn.addMouseListener(new MouseAdapter() {
            
            public void mouseEntered(MouseEvent e) {
                playBtn.setForeground(new Color(200, 210, 10));
            }
            
            public void mouseExited(MouseEvent e) {
                playBtn.setForeground(new Color(255, 255, 255));
            }
            
            public void mouseClicked(MouseEvent e) {
                menuPanel.setVisible(false);
                playPanel.setVisible(true);
                optionsPanel.setVisible(false);
            }
        });
        
        // optionsBtn action
        optionsBtn.addMouseListener(new MouseAdapter() {

            public void mouseEntered(MouseEvent e) {
                optionsBtn.setForeground(new Color(200, 210, 10));
            }
            
            public void mouseExited(MouseEvent e) {
                optionsBtn.setForeground(new Color(255, 255, 255));
            }
            
            public void mouseClicked(MouseEvent e) {
                menuPanel.setVisible(false);
                playPanel.setVisible(false);
                optionsPanel.setVisible(true);
            }
        });
        
        // quitBtn action
        quitBtn.addMouseListener(new MouseAdapter() {

            public void mouseEntered(MouseEvent e) {
                quitBtn.setForeground(new Color(200, 210, 10));
            }
            
            public void mouseExited(MouseEvent e) {
                quitBtn.setForeground(new Color(255, 255, 255));
            }
            
            public void mouseClicked(MouseEvent e) {
                System.exit(0);
            }
        });
    }
    
    public void tick() {
        mainPanel.getSize(new Dimension(currWidth, currHeight));
        
        System.out.println(currWidth + ", " + currHeight);
    }
    
    public void render() {
        
    }
}

您可以使用 JPanel 中的方法 paintComponent(Graphics g)drawImage() 来绘制图像。

import java.awt.*;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.*;
public class AutoScale extends JFrame{
    
private Image image;

    public AutoScale() {
        setTitle("AutoScale");
        setResizable(true);
        setSize(400,400);
                try {
                    image = ImageIO.read(new File("path to your file"));
        }catch(IOException e) {
        System.out.println("Image not found");
                 }

        JPanel panelImg = new JPanel() {

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

                g.drawImage(image, 30, 30, getWidth()/2, getHeight()/2, null);
                }
        };
        add(panelImg);
    }
    
    public static void main(String[] args) {
        
        AutoScale frame = new AutoScale();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
}

在这个例子中,我创建了 Panel 和 paintComponent()。在该方法中,我使用 6 个参数调用 drawImage()

  1. 图片
  2. x坐标
  3. y坐标
  4. Frame 的宽度除以 2(您可以通过添加、减去或除以
    的结果来调整图像的大小 getWidth())
  5. Frame的高度除以2(与宽度相同)
  6. imageObserver,一般设置为null

只要 Panel 的大小发生变化,paintComponent() 方法就会自动调用,因此无需像我之前建议的那样使用 WindowListener。

注意:我使用 try-catch 块,因为如果找不到文件,它会抛出异常。

希望对您有所帮助!

Swing 基于 parent/child 关系。

所以如果你想让按钮显示在背景上,你的代码结构需要是:

- frame
    - background component
        - buttons panel

最简单的方法是使用 JLabel 将您的图片作为背景。然后将按钮面板添加到标签。唯一的问题是默认情况下 JLabel 不使用布局管理器,因此您需要查看布局管理器才能达到您想要的效果。

我建议使用 GridBagLayout,这样按钮就会在面板上居中。基本代码为:

JPanel buttons = new JPanel();
buttons.add(...);

JLabel background = new JLabel(...);
background.setLayout( new GridBagLayout() );
background.add(buttons, new GridBagConstraints());

标签将以背景图片的大小显示。

如果您希望背景图像随着帧大小的变化而缩放,那么您有两个选择:

  1. 使用Stretch Icon。它会自动将图像缩放到 space 可用。
  2. 用 JPanel 替换 JLabel 并自己绘制图像。查看可以配置为自动缩放图像的 Background Panel

编辑:

i tried reading the code and its really confusing.

好吧,我们的目的不是让您阅读代码。目的是让您使用代码。

当您编程时,您将学习如何使用 classes 和 class 的方法。当您使用 ImageIcon class 时,您是先阅读代码还是只是学习如何使用其构造函数?

现在我同意,这两个 classes 没有发布 API 但你真的只需要了解 classes 的构造函数和方法就可以了使用它们。

如果您阅读 Stretch Icon 博客,它指出:

StretchIcon 是 ImageIcon 的 drop-in 替代品,它扩展了它,除了不支持 ImageIcon 的 no-arg 构造函数。

所以这意味着如果您通常使用:

JLabel background = new JLabel( new ImageIcon("background.jpg") );

您将对 StretchIcon 使用以下内容:

JLabel background = new JLabel( new StretchIcon("background.jpg") );

BackgroundPanel 类似,如果您阅读博客,它指出它是:

JPanel 的扩展,为图像的绘制提供一些自定义绘画支持

然后它继续说默认是按你想要的“缩放”绘制图像。所以你需要弄清楚的是使用哪个构造函数来创建面板。

对于常规面板,您将使用:

JPanel background = new JPanel();

对于 BackgroundPanel 来说,最简单的构造函数是 class 的第一个构造函数,它只需要一个 Image 作为参数:

JPanel background = new BackgroundPanel(image);

现在您有了一个面板,只需将 3 个按钮添加到面板即可。

我没有写StretchIcon class所以我不知道代码的细节,我不关心细节只要class做我期望的事。

我确实写了 BackgroundPanel class 所以如果您有具体问题,我可能会帮助您。但我没有时间猜测您觉得代码的哪一部分令人困惑。

编辑 2:

I have 3 buttons and i want them to be at the cemter and stretch too so they stay at the center of the image

这是关于学习如何使用布局管理器的。我从不使用 IDE 来生成我的代码。我想完全控制代码。这使您的代码更清晰,更易于维护。

这允许您为作业选择合适的布局管理器,并允许您轻松地嵌套具有不同布局管理器的面板。在这种情况下,您希望使用 GridBagLayout,默认情况下,它会将添加到其中的任何组件水平和垂直居中。

默认情况下 BackgroundPanel 使用 BorderLayout。但是您可以轻松地将其更改为使用 GridBagLayout。然后我将使用带有 GridLayout 的第二个面板作为按钮。

所以代码应该是这样的:

JPanel buttonPanel = new JPanel( new GridLayout(0, 1, 10, 0) );
buttonPanel.add(playBtn);
...

backgroundPanel.add(buttonPanel, new GridBagConstraints());

现在随着框架大小的改变,按钮将自动 re-center。

阅读 Layout Managers 上的 Swing 教程部分以获取更多信息和示例。

将 link 的 Swing 教程放在手边。它包含大多数 Swing 基础知识的信息和示例。