我正在尝试使用多线程来找到影响JavaFX GUI元素的形状和内容的有效方法,例如简单Pane
。假设我有一个简单的Pane
,我在给定的时间点上显示填充Circle
,我希望能够回答它们,例如通过点击相应的键。到目前为止,为此目的,我尝试使用类实现Runnable
接口并在其中创建经典Thread
对象,以便从外部JavaFX {{添加和/或删除元素} 1}},它从主Pane
类的构造函数参数传递给“线程类”。比如,两个类,1)应用程序和2)线程类,看起来像这样:
Application
...和“线程类”
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class ClassApplication extends Application {
private Pane pane;
public Parent createContent() {
/* layout */
BorderPane layout = new BorderPane();
/* layout -> center */
pane = new Pane();
pane.setMinWidth(250);
pane.setMaxWidth(250);
pane.setMinHeight(250);
pane.setMaxHeight(250);
pane.setStyle("-fx-background-color: #000000;");
/* layout -> center -> pane */
Circle circle = new Circle(125, 125, 10, Color.WHITE);
/* add items to the layout */
pane.getChildren().add(circle);
layout.setCenter(pane);
return layout;
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setWidth(300);
stage.setHeight(300);
stage.show();
/* initialize custom Thread */
ClassThread thread = new ClassThread(pane);
thread.execute();
}
public static void main(String args[]) {
launch(args);
}
}
但是,这样的解决方案,在Swing应用程序中是可能的,在JavaFX中是不可能的,而且还有以下异常的原因:
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
public class ClassThread implements Runnable {
private Thread t;
private Pane pane;
ClassThread(Pane p) {
this.pane = p;
t = new Thread(this, "Painter");
}
@Override
public void run() {
try {
Thread.sleep(2000);
Circle circle = new Circle(50, 50, 10, Color.RED);
pane.getChildren().clear();
pane.getChildren().add(circle);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
public void execute() {
t.start();
}
}
...“21”行是:Exception in thread "Painter" java.lang.IllegalStateException: Not on FX application thread; currentThread = Painter
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Unknown Source)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(Unknown Source)
at javafx.scene.Parent$2.onProposedChange(Unknown Source)
at com.sun.javafx.collections.VetoableListDecorator.clear(Unknown Source)
at ClassThread.run(ClassThread.java:21)
at java.lang.Thread.run(Unknown Source)
我得出结论,“从另一个线程的级别影响主JavaFX线程存在问题”。但是在这种情况下,如果我不能(更准确地说将“我不知道怎
更新时间:2014/08/07,03:42
在阅读给定答案后,我尝试在代码中实现给定的解决方案,以便在每个显示之间以指定的时间间隔在不同的位置显示10个自定义pane.getChildren().clear();
:
Circle
上面的解决方案很好用。非常感谢你。
答案 0 :(得分:7)
除了使用低级Thread
API和Platform.runLater(...)
来安排在FX应用程序线程上执行的代码之外,就像在Tomas的回答中一样,另一个选择是使用FX并发API 。这提供了Service
和Task
类,它们旨在在后台线程上执行,以及保证在FX应用程序线程上执行的回调方法。
对于您的简单示例,这看起来有点过多的样板代码,但对于真正的应用程序代码来说,它非常好,在后台任务和完成时执行的UI更新之间提供了一个干净的分离。此外,Task
可以提交给Executor
。
import javafx.application.Application;
import javafx.concurrent.Task ;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class ClassApplication extends Application {
private Pane pane;
public Parent createContent() {
/* layout */
BorderPane layout = new BorderPane();
/* layout -> center */
pane = new Pane();
pane.setMinWidth(250);
pane.setMaxWidth(250);
pane.setMinHeight(250);
pane.setMaxHeight(250);
pane.setStyle("-fx-background-color: #000000;");
/* layout -> center -> pane */
Circle circle = new Circle(125, 125, 10, Color.WHITE);
/* add items to the layout */
pane.getChildren().add(circle);
layout.setCenter(pane);
return layout;
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setWidth(300);
stage.setHeight(300);
stage.show();
Task<Void> task = new Task<Void>() {
@Override
public Void call() throws Exception {
Thread.sleep(2000);
return null ;
}
};
task.setOnSucceeded(event -> {
Circle circle = new Circle(50, 50, 10, Color.RED);
pane.getChildren().setAll(circle);
});
new Thread(task).run();
}
public static void main(String args[]) {
launch(args);
}
}
如果您所做的只是暂停,您甚至可以使用(或滥用?)动画API。有PauseTransition
暂停指定的时间,您可以使用其onFinished
处理程序执行更新:
import javafx.application.Application;
import javafx.animation.PauseTransition ;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration ;
public class ClassApplication extends Application {
private Pane pane;
public Parent createContent() {
/* layout */
BorderPane layout = new BorderPane();
/* layout -> center */
pane = new Pane();
pane.setMinWidth(250);
pane.setMaxWidth(250);
pane.setMinHeight(250);
pane.setMaxHeight(250);
pane.setStyle("-fx-background-color: #000000;");
/* layout -> center -> pane */
Circle circle = new Circle(125, 125, 10, Color.WHITE);
/* add items to the layout */
pane.getChildren().add(circle);
layout.setCenter(pane);
return layout;
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setWidth(300);
stage.setHeight(300);
stage.show();
PauseTransition pause = new PauseTransition(Duration.millis(2000));
pause.setOnFinished(event -> pane.getChildren().setAll(new Circle(50, 50, 10, Color.RED)));
pause.play();
}
public static void main(String args[]) {
launch(args);
}
}
如果您需要多次执行暂停,可以使用Timeline
,然后拨打setCycleCount(...)
:
import javafx.application.Application;
import javafx.animation.Timeline ;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration ;
public class ClassApplication extends Application {
private Pane pane;
public Parent createContent() {
/* layout */
BorderPane layout = new BorderPane();
/* layout -> center */
pane = new Pane();
pane.setMinWidth(250);
pane.setMaxWidth(250);
pane.setMinHeight(250);
pane.setMaxHeight(250);
pane.setStyle("-fx-background-color: #000000;");
/* layout -> center -> pane */
Circle circle = new Circle(125, 125, 10, Color.WHITE);
/* add items to the layout */
pane.getChildren().add(circle);
layout.setCenter(pane);
return layout;
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setWidth(300);
stage.setHeight(300);
stage.show();
KeyFrame keyFrame = new KeyFrame(Duration.millis(2000),
event -> pane.getChildren().setAll(new Circle(50, 50, 10, Color.RED)));
Timeline timeline = new Timeline(keyFrame);
// Repeat 10 times:
timeline.setCycleCount(10);
timeline.play();
}
public static void main(String args[]) {
launch(args);
}
}
答案 1 :(得分:2)
您只能从JavaFX应用程序线程访问场景。您需要在Platform.runLater()
:
Platform.runLater(() -> {
Circle circle = new Circle(50, 50, 10, Color.RED);
pane.getChildren().clear();
pane.getChildren().add(circle);
});