【问题标题】:Delay when adding JLayerPane to JFrame将 JLayerPane 添加到 JFrame 时延迟
【发布时间】:2021-05-04 09:02:21
【问题描述】:

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

public class Game_Screen extends JLayeredPane {
    
    private JLabel hangman;
    
    public Game_Screen() {
        super();
        super.setBounds(0, 0, 1920, 1080);
        
        hangman = new JLabel(new ImageIcon(Game_Screen.class.getResource("/images/hangman10.jpg")));
        hangman.setBounds(0, 0, 1920, 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", Font.PLAIN, 30));
        add(score, Integer.valueOf(1));
        
        start = new JButton("START");
        start.setBounds(635,400,400,120);
        start.setBorderPainted(false);
        start.setFont(new Font("Press Start", Font.PLAIN, 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());
            
        }
        
    }

}

【问题讨论】:

  • 我会使用CardLayout 在主屏幕/级别屏幕之间切换
  • 我可以将布局添加到 JFrame 中吗?我也可以使用带有 LayeredPane 的布局作为组件吗?
  • CardLayout 将被添加到主组件中,是的,JLayeredPane 可以在布局管理器中使用。我个人的做法是制作一系列刽子手的图像,然后简单地将icon更改为JLabel,可能会更容易更简单
  • 那我试试,谢谢。我确实有一系列刽子手图像,但我还没有添加它们,我只使用一个图像进行测试。使用 CardLayout 是否也有助于解决我遇到的延迟问题?

标签: java swing jlayeredpane


【解决方案1】:

我要在这里和你有点麻烦,坚持住,我相信你会很感激的。

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

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

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

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

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

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

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

相反,在以下示例中,我在HangmanGame 上使用了CardLayout,并为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", Font.PLAIN, 30));
            add(score, Integer.valueOf(1));

            start = new JButton("START");
            start.setBounds(635, 400, 400, 120);
            start.setBorderPainted(false);
            start.setFont(new Font("Press Start", Font.PLAIN, 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, 0, 1920, 1080);

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

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

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

【讨论】:

  • [HangmanGame.]setSize(new Dimension(1920, 1080)); TL:DR;框架太小。 -- 由于游戏的许多内部元素(包括图像)都应该是那个精确的大小,只是指出框架 (HangmanGame) 需要更大以考虑窗口装饰。话虽如此,我注意到您坚持使用 OP 的布局(咳嗽)逻辑,因此与此相比,这可能是一个小问题。 ;)
  • 成功了。 CardLayout 非常有用,总体而言,您的实现很有意义。谢谢。
  • @AndrewThompson 是的,这有点匆忙。我确实更新了Game_Screen 以覆盖getPreferredSize,但我同意
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-29
相关资源
最近更新 更多