简体   繁体   English

如何在JavaFX应用程序内部等待JFrame线程完成?

[英]How to wait for a JFrame thread to finish from inside a JavaFX application?

I'm trying to create a JFrame window from inside a JavaFX application. 我正在尝试从JavaFX应用程序内部创建JFrame窗口。 I'm using Oracle Java 8 and I'm able to do what I need in Linux, Windows but not in Mac OS. 我正在使用Oracle Java 8,并且能够执行Linux,Windows而不是Mac OS中所需的操作。 It seems like a JVM implementation issue. 似乎是JVM实现问题。 Here are my requirements: 这是我的要求:

  • I need to call a library method which creates a game window(an extended class from JFrame). 我需要调用一个创建游戏窗口的库方法(来自JFrame的扩展类)。 I need to be able to use keyboard in game. 我需要能够在游戏中使用键盘。
  • I can access the source code of the library but I don't have any intention to change the source code of the library 我可以访问该库的源代码,但无意更改该库的源代码
  • I need to process game information after the game finished, so my main thread needs to wait until the game finishes 游戏结束后,我需要处理游戏信息,因此我的主线程需要等待游戏结束。

I created sample application below. 我在下面创建了示例应用程序。 Application works fine under Linux and Windows, but in Mac OS when I try to wait for the JFrame thread to finish app freezes and JFrame is not shown. 应用程序在Linux和Windows下运行良好,但是在Mac OS中,当我尝试等待JFrame线程完成应用程序冻结时,未显示JFrame。 SwingUtilities.invokeLater doesn't freeze but it doesn't wait for JFrame thread to finish. SwingUtilities.invokeLater不会冻结,但不会等待JFrame线程完成。 Since I need to wait for the JFrame thread invokeLater is not an option for me. 由于我需要等待JFrame线程,invokeLater并不是我的选择。

JFrameCreator.java JFrameCreator.java

public class JFrameCreator implements Runnable {
    @Override
    public void run() {
        System.out.println("Creating JFrame");
        JFrame frame=new JFrame();
        frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
        frame.setSize(300,300);
        frame.setVisible(true);
        for (int i = 0; i < 100; i++) {
            frame.getContentPane().getGraphics().drawOval(150-i/2,150-i/2,i,i);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

sample.fxml sample.fxml

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="100.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
   <children>
      <Button layoutX="51.0" layoutY="10.0" mnemonicParsing="false" onAction="#createJFrame" text="New JFrame" />
      <RadioButton fx:id="cbThread" layoutX="10.0" layoutY="40.0" mnemonicParsing="false" selected="true" text="Thread">
         <toggleGroup>
            <ToggleGroup fx:id="tg" />
         </toggleGroup>
      </RadioButton>
      <RadioButton fx:id="cbInvokeWait" layoutX="10.0" layoutY="60.0" mnemonicParsing="false" text="invokeAndWait" toggleGroup="$tg" />
      <RadioButton fx:id="cbInvokeLater" layoutX="10.0" layoutY="80.0" mnemonicParsing="false" text="invokeLater" toggleGroup="$tg" />
   </children>
</AnchorPane>

Controller.java Controller.java

public class Controller {
    @FXML RadioButton cbThread;
    @FXML RadioButton cbInvokeWait;
    @FXML RadioButton cbInvokeLater;

    public void createJFrame(javafx.event.ActionEvent actionEvent) {
        Runnable r=new JFrameCreator();
        try {
            if (cbThread.isSelected()) {
                Thread t=new Thread(new JFrameCreator());
                t.start();
                t.join();
            }
            else if(cbInvokeWait.isSelected())
                SwingUtilities.invokeAndWait(r);
            else
                SwingUtilities.invokeLater(r);
        }
         catch (InterruptedException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

Main.java Main.java

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Swing in JavaFX");
        primaryStage.setScene(new Scene(root, 200, 100));
        primaryStage.show();
    }


    public static void main(String[] args) {
        launch(args);
    }
}

I also tried a while loop with Thread.isAlive() method but that also behaves like Thread.join(). 我还尝试了Thread.isAlive()方法的while循环,但它的行为也类似于Thread.join()。 How can I achieve my requirements? 如何达到我的要求?

You could use one of the synchronization classes such as CountDownLatch . 您可以使用同步类之一,例如CountDownLatch Pass it to your Runnable and call latch.countDown() at the end of the run() method. 将其传递给您的Runnable并在run()方法的末尾调用latch.countDown() Meanwhile, your JavaFX thread would be paused on latch.await() . 同时,您的JavaFX线程将暂停在latch.await() This will freeze the JavaFX UI, however, so this might not be what you want. 但是,这将冻结JavaFX UI,所以这可能不是您想要的。

Another option would be to use a callback. 另一种选择是使用回调。 Something like: 就像是:

public class JFrameCreator implements Runnable {

    private final Runnable onFinished;

    public JFrameCreator(Runnable onFinished) {
        this.onFinished = onFinished;
    }

    @Override
    public void run() {
        // do your work...
        Platform.runLater(onFinished); // to communicate to JavaFX Application
                                       // Thread that JFrameCreator is complete
    }

}

This second option won't cause the JavaFX thread to wait but it does allow you to react at the appropriate time. 第二个选项不会导致JavaFX线程等待,但它确实允许您在适当的时间做出反应。 You could change the type of onFinished to whatever you need; 您可以将onFinished的类型更改为onFinished的任何内容。 such as a Consumer if you want to pass the callback some object. 例如Consumer如果您想传递回调某些对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM