【问题标题】:GUI threading in javaJava中的GUI线程
【发布时间】:2012-03-18 17:12:24
【问题描述】:

我正在尝试用 Java 编写一个简单的游戏。基本结构是单个JFrame 和不同的JPanels,我在不同时间添加/删除。在启动时,有一个JPanel 这是一个基本菜单(开始游戏、高分等)。一旦按下“开始”按钮,它就会切换到一个带有三个按钮的级别选择面板,以选择游戏的难度级别。一旦按下三个按钮中的任何一个,它就会切换到另一个面板,该面板将显示三秒倒计时,然后是实际游戏。三个按钮都调用同一个方法,只是传入的难度值不同。

我的所有独立部分都工作正常,但我在从关卡选择面板过渡到倒计时时遇到了麻烦。如果我不使用线程,屏幕会在按钮按下时冻结并且不会切换到新面板。我试过搞乱线程,但我对它们了解不多,而且只取得了有限的成功(我已经得到它,所以它会在某些时候成功切换,但不是始终如一)。

在代码方面,在关卡选择面板中,我有这样的东西来监听按钮的点击:

private class ButtonClickedListener implements ActionListener {
    public void actionPerformed(ActionEvent evt) {  
        gui.newLevel(1);
    }
}

在哪里只是 gui.newLevel(1) 我已经搞砸了启动新线程并从它们调用方法。

newLevel() 方法如下所示:

getContentPane().removeAll();

levelPanel = new LevelPanel(levelNum, this);
add(levelPanel);
validate();

levelPanel.start();

从开始菜单JPanel 切换到关卡选择器面板时,我使用了非常相似的代码(同样,按钮上有一个ActionListener),效果很好。

LevelPanel 的 start() 方法初始化新的 JPanel 的值并在屏幕上显示倒计时(目前使用以下代码,尽管我在显示实际游戏之前将类似的东西放在 newLevel() 方法中搞砸了) :

try {
    Thread.sleep(1000);
    //update countdown number
    validate();
    repaint();
    Thread.sleep(1000);
    //update countdown number
    validate();
    repaint();
    Thread.sleep(1000);
    //update countdown number
    validate();
    repaint();
} catch (Exception e) {
    System.out.println(e);
}

//start game

我非常感谢任何帮助让这段代码工作,我很确定某种线程是要走的路,但我不太确定在哪里/如何。任何建议和/或代码示例都会很棒!

提前致谢!

编辑:我最终使用计时器而不是 Thread.sleep() 重写了倒计时,这解决了部分问题,我最终发现了其余部分,并且与 GUI 的东西完全无关,这就是为什么我一开始没有考虑检查它的原因。

【问题讨论】:

    标签: java multithreading swing user-interface


    【解决方案1】:

    在 EDT 期间从来没有真正使用过Thread.sleep(1000);,导致 GUI 冻结的这段代码是 un_resposible,直到一个新事件调用 EDT 或鼠标悬停也可以激活这个容器

    1) 有两种方法可以在 Swing GUI 中处理任何事件,通过实现

    • Swing Timer

    • SwingWorker 中使用Thread.sleep(1000); 进行延迟

    【讨论】:

    【解决方案2】:

    布局和绘画必须在 EDT 中完成。使用SwingUtilities.invokeAndWait 调用validate()repaint()

    【讨论】:

    • 另外,我应该在我的代码中具体在哪里使用 invokeAndWait()?
    • invokeAndWait 容易导致死锁。 invokeLater 通常是一个更好的主意。
    【解决方案3】:

    您可以使用 TimerTask 启动一些具有时间延迟的代码:

    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        public void run() {
            invokeLater(); // This starts after [delay] ms 
            // and - if given - will run every [period] ms.
        }
    }, delay, period);
    

    你可以用这个解决你的问题,虽然它不是一个很好的解决方案。

    // 编辑:(见 cmets)你应该正确同步对 gui 的访问,否则它会给你错误。

    【讨论】:

    • 请编辑您的帖子并将doSomething(); 包装成invokeLater(),否则您可以等待一些down_votes 对您真正错误的答案进行投票,从而导致相同的(与OP 的)问题相同..
    • “这不是一个很好的解决方案。” 除非在 EDT 上完成 GUI 更新,否则它根本不是一个解决方案。这就是javax.swing.TimerSwingWorker 派上用场的地方。
    • @mKorbel 打败我!正确的答案和正确的评论。 :)
    • 这在我的代码中具体用于什么?倒计时?
    • 是的,我会用它来进行倒计时,也可能是游戏延迟开始。但要小心,这在你的 GUI 之外的另一个线程中运行,如果你更新标签或其他东西,你必须使用像上面写的 invokeLater() 这样的东西。
    猜你喜欢
    • 2012-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-12
    • 1970-01-01
    • 2011-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多