将 JLayerPane 添加到 JFrame 时出现延迟

问题描述

我需要使用 Swing 用 Ja​​va 制作一个刽子手游戏。我在 photoshop 中制作了我的刽子手人物,并使用完整图像作为背景层,然后使用 JlayeredPane 将其他组件放在顶部。但是,当我添加 JFrame 并打开窗口时,总是会有一点延迟。我尝试创建一个按钮,以便在单击时在 JlayeredPane 上添加删除,但这导致了更大的延迟。任何帮助将不胜感激。

public class Game_Screen extends JlayeredPane {
    
    private JLabel hangman;
    
    public Game_Screen() {
        super();
        super.setBounds(0,1920,1080);
        
        hangman = new JLabel(new ImageIcon(Game_Screen.class.getResource("/images/hangman10.jpg")));
        hangman.setBounds(0,1080);
        
        add(hangman,Integer.valueOf(0));
    }
}

游戏中一个窗格的示例

public class Home_Screen extends Game_Screen {
    
    private JLabel title;
    private JLabel score;
    private JButton start;
    
    public Home_Screen() {
        super();
        title = new JLabel("HANGMAN");
        title.setBounds(490,170,942,217);
        title.setFont(new Font("Press Start",Font.PLAIN,100));
        add(title,Integer.valueOf(1));
        
        score = new JLabel("High score: 0");
        score.setBounds(635,330,470,67);
        score.setFont(new Font("Press Start",30));
        add(score,Integer.valueOf(1));
        
        start = new JButton("START");
        start.setBounds(635,400,120);
        start.setBorderPainted(false);
        start.setFont(new Font("Press Start",60));
        add(start,Integer.valueOf(1));
    }
    
    public void addStartButtonListener(ActionListener a) {
        start.addActionListener(a);
    }
}

JFrame 类

public class HangmanGame extends JFrame {
    
    private Home_Screen home;
    private Level_Screen level;
    private Win_Screen win;
    private Lose_Screen lose;
    private Play_Screen play;
    
    public HangmanGame() {
        super("Hangman");
        
        home = new Home_Screen();
        level = new Level_Screen();
        win = new Win_Screen();
        lose = new Lose_Screen();
        play = new Play_Screen();
        add(home);
        setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE);
        setSize(new Dimension(1920,1080));
        setLayout(null);
        setVisible(true);


    }
    
    public Home_Screen getHomeScreen() {
        return home;
    }
    
    public Level_Screen getLevelScreen() {
        return level;
    }
    
    public Win_Screen getWinScreen() {
        return win;
    }
    
    public Lose_Screen getLoseScreen() {
        return lose;
    }
    
    public Play_Screen getPlayScreen() {
        return play;
    }
}

控制器类

public class Controller {
    
    private HangmanGame game;
    
    public Controller() {
        //game = g;
        game = new HangmanGame();
        game.getHomeScreen().addStartButtonListener(new startButtonListener());
    }
    
    class startButtonListener implements ActionListener{

        @Override
        public void actionPerformed(ActionEvent e) {
            game.remove(game.getHomeScreen());
            game.add(game.getLevelScreen());
            
        }
        
    }

}

解决方法

我要在这里打扰你一下,别着急,我相信你会很感激的。

首先,Swing 是懒惰的。当您对组件或布局进行更改时,您需要告诉 Swing 您希望更新内容。

这意味着,当您添加或删除组件时,通常会调用 revalidaterepaint。由于您没有大量使用布局管理器,因此您可以离开 revalidate

不过。这里一个更简单的解决方案是使用 CardLayout。这允许您根据需要在视图之间“翻转”。

我对你的方法有一个明显的问题,就是责任的概念。谁实际负责什么。您 Controller 正在对应该发生的事情做出很多决定,如果您正确使用控制器,这可能不是一个坏主意,但是,另一个想法可能是使每个视图独立并利用改为观察者/委托模式。

例如,您并不真正关心 Home_Screen 的“如何”开始,只关心它可以。观察者会想知道“开始”何时发生并采取适当的行动。

你已经用 Controller 做到了这一点,但我对 Controller 对组件有深刻理解的想法感到畏缩,这只是我;)

相反,我希望事情尽我所能地变得愚蠢,所以如果我需要改变事情,并不是每次都完全重新编写。

相反,在下面的示例中,我在 CardLayout 上使用了 HangmanGame 并向 Home_Screen 提供了一个“委托”,它告诉我们何时发生“开始”(我不在乎如何,只是它确实如此)然后允许 HangmanGame 控制导航。

这通常是一件小事,您可能会争辩说可以使用某种“导航控制器”,但是您需要这样做,这确实暴露了实现细节并且......这是一团糟...... :P

import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;

public class Controller {

    private HangmanGame game;

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

    public Controller() {
        //game = g;
        game = new HangmanGame();
    }

    public class HangmanGame extends JFrame {

        private Home_Screen home;
        private JPanel level;

        public HangmanGame() {
            super("Hangman");

            CardLayout cardLayout = new CardLayout();

            home = new Home_Screen(new HomeDelegate() {
                @Override
                public void startGame() {
                    cardLayout.show(getContentPane(),"level");
                }
            });
            level = new JPanel();
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setSize(new Dimension(1920,1080));
            setLayout(cardLayout);
            add(home,"home");
            add(level,"level");
            setVisible(true);

        }
    }

    public interface HomeDelegate {

        public void startGame();
    }

    public class Home_Screen extends Game_Screen {

        private JLabel title;
        private JLabel score;
        private JButton start;

        public Home_Screen(HomeDelegate delegate) {
            super();
            title = new JLabel("HANGMAN");
            title.setBounds(490,170,942,217);
            title.setFont(new Font("Press Start",Font.PLAIN,100));
            add(title,Integer.valueOf(1));

            score = new JLabel("High Score: 0");
            score.setBounds(635,330,470,67);
            score.setFont(new Font("Press Start",30));
            add(score,Integer.valueOf(1));

            start = new JButton("START");
            start.setBounds(635,400,120);
            start.setBorderPainted(false);
            start.setFont(new Font("Press Start",60));
            add(start,Integer.valueOf(1));

            start.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    delegate.startGame();
                }
            });
        }
    }

    public class Game_Screen extends JLayeredPane {

        private JLabel hangman;

        public Game_Screen() {
            super();

            hangman = new JLabel(new ImageIcon(getClass().getResource("/images/hangman10.png")));
            hangman.setBounds(0,1920,1080);

            add(hangman,Integer.valueOf(0));
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(1920,1080);
        }
    }
}

现在,Swing 预计会出现初始加载延迟,因为事件调度线程可能需要几秒钟才能启动并完成所有必需的初始化,但其中一部分“可能”是图像的加载,不过,在我开始花大量时间担心之前,我会专注于让您的游戏正常运行