【问题标题】:Why isn't this gui updating?为什么这个 gui 不更新?
【发布时间】:2013-12-27 20:23:36
【问题描述】:

我有一个Gui 和一个Game 课程,但我无法从游戏中更新 gui。我没有使用线程,但我以前见过它更新,所以这不是问题。游戏逻辑真的很简单,不需要线程。不管我怎么叫repaint()revalidate(),现在不管我把它放在哪里都不管用。

class Gui {

    //...

    public Gui(Game game) {
        this.game = game;
        initialize();       
    }

    private void initialize() {
        //...
        okButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent arg0) {
                okAction(textField.getText());

                textField.setVisible(false);
                okButton.setVisible(false);
                textField.setText("");
            }           
        });
    }

    private void okAction(String input) {
        game.receiveInput(input);
    }

    public void output(String msg) {        
        textArea.append(msg + "\n");        
    }

    public void getInput() {
        textField.setVisible(true);
        okButton.setVisible(true);
        textField.setText("");      
    }
}

我还希望能够将String 传递回游戏实例。我想我会在游戏中调用getInput(),它会显示JTextField 用于输入,JButton 用于提交。在actionPerformed() 方法中,我只需要输入文本,然后在游戏类中调用一个方法。我不知道这是否可行,因为 gui 没有更新,而且我从未出现过输入字段和按钮。是这样吗?

这将是 gui “回调”的方法:

class Game {

    //...

    public void receiveInput(String input) {
        int n = Integer.parseInt(input);
        if ( validInput(input, actualDecision.choices.size()) ) {
            parser.setAction(actualDecision.choices.get(n-1).action);
        }
    }
}

在游戏课上,我只想打几次gui.output()gui.getInput()

我的问题在哪里?为什么它不更新,也不冻结?如果我使用调试器,output()getInput() 都会被执行,但没有任何反应......

编辑:

好的,我自己发现了一个问题,获取输入部分...由于它返回很快,它永远无法接收输入。但这并不能解释为什么没有显示输入字段和按钮,或者没有显示任何文本

编辑 2:

天哪,对不起,我真的不知道如何让它更短,但你只需要看看GameGui,其他的就在那里编译。

代码:https://gist.github.com/anonymous/53bad714592792316b4d

要测试的 xml:https://gist.github.com/anonymous/30b56facb78fe6ecd482

【问题讨论】:

  • 您应该在 EDT 线程中进行 GUI 操作。
  • @RossDrew 假设所有逻辑都可以在“tick”内完成(多长时间;1/60 秒是理想的)不需要线程
  • 您也可以发布 gui.output() 吗?最好作为一个完整的(小)程序
  • 假设,出于未知原因,Game 类的 receiveInput(String input) 函数永远不会被调用。现在向我们展示调用代码:无论您将它们放在哪里。做一个SSCCE
  • @RossDrew 实际上是正确的。理查德我写了一个答案,反映了 EDT 机制和this answer with linked documentation 中的SwingUtilities 类。想看的可以去看看。

标签: java swing user-interface


【解决方案1】:

老实说,我刚刚查看了Gui 类代码,不知道为什么它在与Game 类交互时不能正确更新。但是我对你的代码有几点意见,我希望这些可以通过使这个类更简单来引导你以正确的方式,然后你可以专注于与Game类的交互。

菜单栏

您将JMenuBar 添加到JPanel,而不是将其设置为JFrame

panel.add(menuBar, gbc);
//frame.setJMenuBar(menuBar); Use this instead

由于JFrame 是一个准备处理菜单栏的顶级容器,您应该考虑进行建议的更改。

保存validate()调用

由于JFrameinitialize() 方法的开始处被初始化并且JPanel 在使框架可见之后被添加,所以您必须调用frame.revalidate() 方法来重新验证组件层次结构。如果您只是在使框架可见之前初始化面板,那么您不需要调用revalidate() 方法。更多详情请查看this answer

缺少pack() 呼叫

没有调用frame.pack() 方法来布置框架的子组件。看看Window.pack()

添加okButton时缺少GridBagConstraints

okButton 添加到panel 时,没有GridBagConstraints 作为参数:

panel.add(okButton);
//panel.add(okButton, gbc); This is the correct way.

使用setSize()

在这一行:

frame.setSize(800, 600);

由于本主题中讨论的原因,我们应该避免使用set(Preferred | Minimum | Maximum)Size()Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?

使用GridBagLayout

这只是一个建议,您使用GridBagLayout 的方式没有任何问题。您可能已经注意到这个布局管理器有点难以使用(顺便说一句,我真的不喜欢它:)您可以使用Nested Layout 方法来使组件布局更容易并使您的代码更具可读性。也许这种方法已经足够好了:

  • JMenuBar 设置为JFrame。布局少了一个组件 ;)
  • 将带有文本区域的滚动窗格直接添加到框架的 内容窗格使用 BorderLayout 约束:frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
  • 创建一个新的JPanel 来保存文本字段和用于 要求用户输入并将其添加到框架的内容窗格中。

翻译成代码:

JPanel usersInput = new JPanel(new FlowLayout(FlowLayout.CENTER));
usersInput.add(textField);
usersInput.add(okButton);

frame = new JFrame();
frame.setJMenuBar(menuBar);
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
frame.getContentPane().add(usersInput, BorderLayout.SOUTH);
frame.setTitle("Choose your own adventure");
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // It's better practice DISPOSE_ON_CLOSE ;)
frame.setVisible(true); 


更新

好吧,我很好奇,我真的很想测试你的游戏(顺便说一句,工作非常好:)问题是我在逻辑中发现了至少两个问题:

使用==比较字符串

Parser 类中有几个使用== 的字符串比较,但这不是比较字符串的正确方法。我们必须使用.equals() 方法来比较字符串是否相等。看看这个话题:How do I compare strings in Java?

Game.processStory() 有一个无限循环

这个方法在这里有一个无限循环:

while ( !end() ) { // this condition never is false so the loop is infinite
    ...
}

更接近Game.end() 方法我发现了一个不正确的字符串比较:

private boolean end() {
    return ( parser.getAction() == "end" );
    //It should be: return parser.getAction().equals("end");
}

但修复它也不能解决问题:它变成 parser.getAction() 总是返回 "d1",因此它永远不会等于 "end"

话虽如此,Game.play()newGameItem 菜单项触发的Event Dispatching Thread(又名 EDT)中执行,Game.processStory() 有这个无限循环,然后 EDT 被阻塞,Gui 永远不会更新.

在这种情况下,我建议您查看Concurrency in Swing trail 以了解如何避免阻塞 EDT。

【讨论】:

  • 感谢您的见解,我真的很感激!我学到了很多东西!
  • @Innkeeper 不客气 :) 请查看我更新的答案。我想我现在发现了问题:)
  • 再次感谢您,我已经纠正了字符串比较发现的错误,正如您可能已经猜到的那样,我对 Java 不太满意...parser.getAction() 仅返回 d1,因为操作将由用户设置,因为这是他的决定。问题是,当调用gui.getInput() 时,输入的额外按钮和文本字段永远不会显示......奇怪的是,现在一切都在EDT 上运行,但gui.output()gui.getInput() 仍然没有改变gui。
  • 如果我通过注释掉gui.getInput() 来停止无限循环,并模拟会发生什么(例如调用receiveInput("1")),它会按预期继续,发现它应该结束,当没有更多工作需要做,把文本区的东西都吐出来了。
  • @Inkeeper 你是对的。问题实际上是所有东西都在 EDT 中运行,这个无限循环阻止它并且不允许更新 gui。是否有意义?你必须想办法在不阻塞 EDT 的情况下进行 Game 和 Gui 之间的交互。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多