Witajcie.
Uczę się podstaw Javy (w tym momencie przerabiam wątki). Chciałem napisać najprostszą appkę w JavieFX która by je wykorzystywała.
Założenia były następujące:
*Tworzę pętlę o określonej liczbie powtórzeń
*Tworzę przycisk START, po naciśnięciu którego zawartość pola Label jest w w/w pętli podmieniana na numer iteracji tejże pętli co sekundę
*Tworzę przycisk STOP, po naciśnięciu którego zawartość pola Label jest podmieniana na napis "Koniec."
Jak już zapewne się domyślacie, po próbie zmiany zawartości pola Label przy pomocy textLabel.setText("Mój nowy napis") w pętli - wszystko kończy się freez'em całości, lub w przypadku użycia wątku wyjątkiem IllegalStateException.
Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
Szukałem dosyć dużo w internecie, ale efekty był raczej mizerne. Odnoszę wrażenie, że dokumentacja JavyFX jest w tym wypadku strasznie uboga, a SWING wypada na tym polu znacznie lepiej.
Wiem już, że zmiany GUI nie mogą być dokonywane w innych wątkach.
Pomijając pełno różnych tutoriali szukałem również pomocy tutaj:
http://docs.oracle.com/javafx/2/api/javafx/application/Platform.html#runLater%28java.lang.Runnable%29
http://docs.oracle.com/javafx/2/api/javafx/concurrent/Task.html
jednak za dużo mi to nie dało.
Jak rozwiązać ten problem? Podejrzewam, że kwestia jest bardzo prosta, ale nie mam pojęcia jak się za to wziąć. Trzeba wątek GUI skomunikować jakoś z wątkiem w którym jest pętla?
Z racji, że nie miałem nigdy wcześniej z czymś takim kontaktu proszę Was o pomoc.
Z góry dziękuję i pozdrawiam :)
Mój kod:
Main.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent parent = FXMLLoader.load(getClass().getResource("sample.fxml"));
Scene scene = new Scene(parent);
primaryStage.setTitle("MT Test");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Core.java
import javafx.scene.control.Label;
public class Core implements Runnable{
private Label textLabel;
public Core(Label textLabel) {
this.textLabel = textLabel;
}
public void changeLabel(){
for(int i = 0; i < 100; i++) {
try {
//No i tutaj jest mój problem
//-----------------------------------
//String tmp = Integer.toString(i + 1);
//textLabel.setText(tmp);
//-----------------------------------
System.out.println(i + 1 + " Thread number: " + Thread.currentThread().getId());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
changeLabel();
}
}
Controller.java
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable{
@FXML
private Label textLabel;
@FXML
private Button startButton;
@FXML
private Button stopButton;
@Override
public void initialize(URL location, ResourceBundle resources) {
startButton.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
Core core = new Core(textLabel);
Thread t = new Thread(core);
t.start();
}
});
stopButton.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
textLabel.setText("Koniec.");
}
});
}
}
sample.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane prefHeight="610.0" prefWidth="820.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<bottom>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<children>
<Button fx:id="startButton" mnemonicParsing="false" prefHeight="75.0" prefWidth="200.0" text="START">
<HBox.margin>
<Insets right="10.0" />
</HBox.margin>
</Button>
<Button fx:id="stopButton" mnemonicParsing="false" prefHeight="75.0" prefWidth="200.0" text="STOP">
<HBox.margin>
<Insets left="10.0" />
</HBox.margin>
</Button>
</children>
</HBox>
</bottom>
<center>
<Label fx:id="textLabel" alignment="CENTER" prefHeight="205.0" prefWidth="696.0" text="Label" BorderPane.alignment="CENTER">
<font>
<Font name="Ubuntu Mono" size="72.0" />
</font>
</Label>
</center>
</BorderPane>