【问题标题】:Lag before JFrame event handlers are added?在添加 JFrame 事件处理程序之前滞后?
【发布时间】:2012-09-05 01:07:28
【问题描述】:

我正在做一个简单的 Java swing 项目。这是主类的代码(改名):

public class MainProg
{
    private static MainProg program; 

    //mainWin is a JFrame
    private MainWindow mainWin;

    //Event handler class which extends MouseAdapter
    private TrayManager trayMgr;


    public static void main(String[] args)
    {                
        program = new MainProg();
    }

    public MainProg()
    {
        mainWin = new MainWindow();
        trayMgr = new TrayManager();

        mainWin.startBtn.addMouseListener(trayMgr);

        mainWin.setVisible(true);
    }
}

很明显,当程序启动时,在main() 中创建MainProg 类的新实例,然后调用构造函数。在构造函数中,它创建了 JFrame mainWin 的新实例。然后它将事件处理程序附加到mainWin 上的按钮。

在事件处理程序类trayMgr中,唯一的方法是mouseClicked(),它什么都不做 除了System.out.println('Clicked');

问题是,当我在 Netbeans 中运行此程序时,会立即显示 JFrame,但我似乎必须单击按钮 2-3 次才能在控制台中打印消息。

这只是 Netbeans 特有的,还是我必须更改某些内容才能在窗口可见之前设置事件处理程序?

【问题讨论】:

  • 您是否有机会添加 SSCCE 来证明问题?
  • 为什么还要向 JButton 添加 MouseListener?你几乎不应该这样做。
  • ActionListener 处理 JButton 的点击。如果您使用 MouseListener,您可能会弄乱按钮的功能。你需要阅读教程,因为有很多东西要学。
  • 再次,最重要的是,摆脱那个 MouseListener 并使用一个 ActionListener。请回来报告,看看是否有任何作用。在您第一次学会手动编写 Swing 代码之前,我自己的偏见是反对使用 NetBeans 为您生成 GUI 代码。它不是一个简单的点击工具,如果您不熟悉 Swing 的基本原理,可能会失败。

标签: java swing netbeans concurrency event-dispatch-thread


【解决方案1】:

您的线程问题不太可能导致您当前的问题,但理论上存在问题的可能性,而且我已经看到一些与一些更敏感的外观和感觉相关的实际问题。很简单,您应该将启动 GUI 的代码排入 Swing 事件线程。你这样做:

public void main(String[] args) {
  SwingUtilities.invokeLater(new Runnable(
    public void run() {
      program = new MainProg();
    }
  ));
}

其他人建议使用invokeAndWait(...) 而不是invokeLater(...),但这可能会有风险,特别是如果您无意中从 Swing 事件线程本身进行了此调用。对于您的情况,您最好使用invokeLater(...)

但是,我认为您所显示的代码的主要问题是在应该使用 ActionListener 的地方不恰当地使用 MouseListener。学习编写任何 GUI 库的代码可能非常棘手,因此,您不能假设任何事情。查看教程并向专家学习。此外,如果您正在考虑长期编写 Swing 代码,请考虑放弃 NetBean 的代码生成实用程序,并首先学习手动编写 Swing 代码。你不会后悔这样做的。

【讨论】:

  • 问题。所以当这段代码运行时,它会变成这样: MainProg() -> JFrame() -> Jframe.initComponents() (这是设置所有 UI 组件并将其添加到 Jframe 的地方)。这是你想要的吗?
  • 我不得不赞同你首先学习如何手动编写 Swing 的建议。我已经做了相当多的工作,并且刚刚开始学习如何使用 NetBeans GUI Builder。我最近在 Stackoverflow 上的许多 Java 问题都是关于使用 GUI Builder 的。总的来说,我觉得它很笨拙,很可能会在未来的项目中完全不再使用它。
  • @ClickUpvote 由于线程问题,调用堆栈比您所说的要复杂一些。最终 run() 被调用,然后调用 MainProg() 构造函数等等。
  • @Click:我不是 100% 确定你在问什么,但我认为是的,这是正确的。我只能肯定地说,所有的 GUI 构建都应该在事件线程上完成。
  • @HovercraftFullOfEels 事件线程是指您提供给上述SwingUtilities.invokeLater 的新线程?
【解决方案2】:

既然你问了,我在另一个主题上发布了 here is a Java SSCCE 的代码。 invokeLater 是在 EDT 上运行计算的一种方式。 (还有invokeAndWait,在这里可以正常工作,但在其他一些情况下可能会导致死锁。)

事实上,这个例子可能有点过于保守了。一些参考资料说您可以从主线程运行 Swing,调用 show()setVisible()。但是,当我尝试时,我有一个在 Java 7 下行为不端的程序。

【讨论】:

  • ,你几乎不应该使用invokeAndWait(...)。最好使用invokeLater(...)。我知道的唯一例外可能是在启动 JApplets 时,但仅此而已。 OP 正在启动一个 JFrame,所以这个建议是错误的。请更改它。
  • 如果你给我一个参考来支持这个断言,就可以了。在长时间搜索 Swing 文档时,我从未见过任何东西。在这里,invokeLater 阻塞了主线程,无论如何都不允许它做进一步的 UI 工作。在我发布的示例中,invokeLater 将让主线程继续执行更多指令以等待 EDT 退出。执行路径没有实质性差异。但是,我很高兴被证明是错误的,而不仅仅是粗体的简单陈述。
  • 我知道的主要问题是你可以在任何线程上调用invokeLater,而你不能在EDT上调用invokeAndWait。如果您想要这方面的文档,只需查看 SwingUtilities API
  • invokeAndWait 也存在导致死锁的风险,您必须确保调用invokeAndWait 的线程在调用发生时不持有其他人可能需要的任何锁。有关这方面的更多信息,请阅读 Hans Muller 和 Kathy Walrath 的Threads and Swing,他们是帮助创建 Swing 及其线程机制的团队的一员。
  • OP 在主线程中运行简单的 Swing 初始化代码,而我对 InvokeAndWait 的建议就是在这种情况下。我同意有人可能会断章取意并遇到麻烦,所以我会改变它。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-04
相关资源
最近更新 更多