如何在不终止JVM的情况下停止Java GUI

时间:2020-06-02 04:31:47

标签: java java-8 jvm windowbuilder

我有一个Java控制台应用程序,可以在用户提供一定的输入后打开一个单独类的GUI应用程序。关闭GUI后,我想返回控制台,这是通过按“退出”按钮实现的,用户可以选择执行其他任务或重复执行该任务。我注意到用System.exit(0)杀死系统会杀死整个过程。我尝试使用线程来工作,但是对此我还比较陌生,而且似乎也不起作用。有人可以指出我正确的方向吗?谢谢。

我也尝试使用frame.dispose()。它确实加载了控制台并按预期方式打印出了在System上调用main方法时的内容,但不允许我输入其他输入。

btnExit.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
     frame.dispose();
     System.main(null);
 }
});

错误消息:Exception in thread "AWT-EventQueue-0" java.util.NoSuchElementException: No line found.

1 个答案:

答案 0 :(得分:4)

不要从侦听器方法中调用您的main方法。这看起来似乎具有预期的效果,但是将main方法运行在事件处理线程上,而为当前事件处理调用的方法仍在堆栈上。这样不仅可以将他们的资源保存在内存中,而且如果您再次尝试打开GUI也会适得其反。

另外,在打开GUI时请注意不要关闭任何与控制台相关的资源(System.inSystem.out),因为当您想再次使用控制台时无法重新打开它们。 / p>

如果我对您的理解正确,那么您的主要方法中就有一个程序流,该程序流不遵循GUI应用程序的事件驱动流,而您想返回该程序流。简单GUI的一种方法是使用模式对话框而不是窗口或框架。

public static void main(String[] args) {
    // a simple console application
    for(boolean exit = false; !exit; ) {
        System.out.println("Enter choice:");
        System.out.println("\t1 - Say hello");
        System.out.println("\t2 - Open GUI");
        System.out.println("\t3 - Exit");
        switch(System.console().readLine()) {
            case "1": sayHello(); break;
            case "2": openGUI(); break;
            case "3": exit = true; break;
            default: System.out.println("Not a valid input");
        }
    }
}

private static void sayHello() {
    String name = System.console().readLine("Enter your name: ");
    System.out.println("Hello "+name);
}

private static void openGUI() { // a temporary GUI application
    JDialog d = new JDialog((Window)null, Dialog.ModalityType.TOOLKIT_MODAL);
    JButton b = new JButton("Exit GUI");
    b.addActionListener(ev -> d.dispose());
    d.getContentPane().add(b, BorderLayout.PAGE_END);
    d.pack();
    d.setVisible(true);
}

对于模式对话框,将要打开的方法调用在关闭之前不会返回。该示例使用dispose()来确保及时释放资源,并且事件处理线程将终止,因此main方法可以像普通控制台应用程序一样通过简单地返回来终止程序。

如果没有选择对话框或涉及到更复杂的GUI,则SecondaryLoop类已概括了让GUI运行直到发生特定事件的逻辑:

public static void main(String[] args) {
    for(boolean exit = false; !exit; ) {
        System.out.println("Enter choice:");
        System.out.println("\t1 - Say hello");
        System.out.println("\t2 - Open GUI");
        System.out.println("\t3 - Exit");
        switch(System.console().readLine()) {
            case "1": sayHello(); break;
            case "2": openGUI(); break;
            case "3": exit = true; break;
            default: System.out.println("Not a valid input");
        }
    }
}

private static void sayHello() {
    String name = System.console().readLine("Enter your name: ");
    System.out.println("Hello "+name);
}

private static void openGUI() {
    JFrame d = new JFrame();
    SecondaryLoop eventLoop = d.getToolkit().getSystemEventQueue().createSecondaryLoop();
    JButton b = new JButton("Exit GUI");
    b.addActionListener(ev -> {
        d.dispose();
        eventLoop.exit();
    });
    d.getContentPane().add(b, BorderLayout.PAGE_END);
    d.pack();
    d.setVisible(true);
    System.out.println("UI created");
    eventLoop.enter();
    System.out.println("UI disposed");
}

由于打开JFrame不会让主线程等待,如后面的打印语句所示,此代码使用SecondaryLoop的{​​{1}}方法进入事件处理该循环仅在按钮的动作侦听器将调用enter()方法时结束。然后,主线程返回并继续处理控制台循环。