Я пытаюсь найти эффективный способ повлиять на форму и содержимое элементов GUI JavaFX, таких как простой Pane
, с использованием многопоточности. Предположим, у меня есть простой Pane
, на котором я показываю заполненные Circle
s при заданных значениях времени, и я хочу иметь возможность ответить на них, например. нажав соответствующий ключ. До сих пор для этой цели я попытался использовать класс с реализацией интерфейса Runnable
и создания в нем классического объекта Thread
, чтобы добавить и/или удалить элементы из внешнего JavaFX Pane
, который был передан этому «классу потоков» "в его параметре конструктора из основного класса Application
. Скажем, оба класса, 1) приложения и 2) класс резьбы, выглядит следующим образом:Каков наилучший способ управления многопотоковой обработкой в JavaFX 8?
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);
}
}
... и "класс резьбы"
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();
}
}
Однако такое решение, где в свинг Приложение может быть возможным, в JavaFX невозможно, и более того, является причиной, за исключением следующего:
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)
... где линия «21» является: pane.getChildren().clear();
Я пришел к выводу, что «существует проблема влияния основного потока JavaFX с уровня другого потока». Но в этом случае, как я могу изменить форму и содержимое элементов графического интерфейса JavaFX динамически, если я не могу (tbh более точно сказать, что это «я не знаю, как»), связывается с несколькими потоками?
ОБНОВЛЕНИЕ: 2014/08/07, 03:42
После прочтения данные ответов я пытался реализовать указанные решения в коде, для того, чтобы отобразить 10 пользовательскими Circle
с на различных местах с заданными временными интервалами между каждый дисплей:
/* in ClassApplication body */
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setWidth(300);
stage.setHeight(300);
stage.show();
Timeline timeline = new Timeline();
for (int i = 0; i < 10; i++) {
Random r = new Random();
int random = r.nextInt(200) + 25;
KeyFrame f = new KeyFrame(Duration.millis((i + 1) * 1000),
new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent ae) {
pane.getChildren().add(new Circle(
random, random, 10, Color.RED));
}
});
timeline.getKeyFrames().add(f);
}
timeline.setCycleCount(1);
timeline.play();
}
Решение выше работает просто отлично. Большое спасибо.
И если злоупотребление анимации API еще слишком много шаблоннога, ReactFX имеет [обертку] (http://www.reactfx.org/javadoc/org/reactfx/util/FxTimer.html) вокруг него : 'FxTimer.runLater (Duration.ofMillis (2000),() -> pane.getChildren(). SetAll (новый круг (50, 50, 10, цвет.RED)));' –
@James_D Проблема с этим решением заключается в том, что он хорош для отдельных событий с задержкой. Но в случае отображения нескольких «Кругов» в определенные промежутки времени это проблематично. Скажем, у меня есть 10 'Circle' и я хочу, чтобы они отображались один за другим в, например. 2000 мс на одной и той же панели. Я также пытался использовать «Timeline», и это «KeyValue» и «KeyFrame», но проблема в том, что в объекте KeyValue я не могу добавлять и удалять элементы из «Панели». – bluevoxel
Добавлен пример, который повторяется 10 раз. Вы можете установить количество циклов в 'Animation.INDEFINITE' для повторения на неопределенный срок. И @TomasMikula, без сомнения, имеет однострочный интерфейс от ReactFX для того же самого;). –