Reputation: 157
My goal is to create a javaFX window that, when clicked, will increment the counter of a singleton class. A background task should read the value in the singleton and display it on the same window. Below is the singleton.
Singleton.java:
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return singleton;
}
//=====================================
private int count = 0;
public void increment() {
count++;
System.out.println(count);
}
public int getCount() {
return count;
}
}
To create the background thread, I've tried creating a task through the initialize() method provided by JavaFX.INITIALIZABLE, but this creates a "Not on FX application thread" error. Below is the main which grabs the FXML and starts the window.
main.java:
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Sample.fxml - Here is where I define window structure. Note the fx:id="counter" for the first label.
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="199.0" prefWidth="230.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
<children>
<AnchorPane layoutX="22.0" layoutY="9.0" prefHeight="182.0" prefWidth="186.0">
<children>
<Button layoutX="14.0" layoutY="76.0" mnemonicParsing="false" onAction="#buttonClicked" text="Increment" />
<Label fx:id="counter" layoutX="131.0" layoutY="81.0" text="0" />
<Label layoutX="28.0" layoutY="34.0" text="First JavaFX Project" />
</children>
</AnchorPane>
</children>
</Pane>
Controller.java - And finally the controller. The initialize function creates a new thread to run the task needed to get the counter value, but it can't edit the original window.
public class Controller implements Initializable {
// Links to Singleton classes
private Singleton sin = Singleton.getInstance();
// Links to FXML elements
@FXML
private Label counter;
public void buttonClicked() {
System.out.print("Click");
sin.increment();
}
@Override
public void initialize(URL arg0, ResourceBundle arg1) {
MyTask myTask = new MyTask();
Thread myTaskThread = new Thread(myTask);
myTaskThread.start();
}
class MyTask extends Task<Void>{
@Override
protected Void call() throws Exception {
while(true) {
int count = sin.getCount();
counter.setText(Integer.toString(count));
}
}
}
}
How can I get this thread to periodically update the FXML elements on the running application?
Upvotes: 0
Views: 740
Reputation:
I believe your line trying to update the text must be used inside this code
Platform.runLater(() -> {insert your code here});
This is what your class MyTask should look like when updating the text value:
class MyTask extends Task<Void>{
@Override
protected Void call() throws Exception {
while(true) {
int count = sin.getCount();
Platform.runLater(() -> {
counter.setText(Integer.toString(count));
});
}
}
}
When trying to update anything on the user interface, you must be on the application thread. The code above tells the program to execute the code on the application thread.
Upvotes: 0
Reputation: 325
Your use case does not need a background thread to be accomplished, you simply need to use the javafx two way binding, and StringProperty
public class Controller implements Initializable {
// Links to Singleton classes
private Singleton sin = Singleton.getInstance();
// Links to FXML elements
@FXML
private Label counter;
private StringProperty singletonCounter;
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
singletonCounter = new SimpleStringProperty();
singletonCounter.set(sin.getCount() + "");
counter.textProperty().bind(singletonCounter);
}
public void buttonClicked() {
sin.increment();
singletonCounter.setValue(sin.getCount() + "");
}
}
Upvotes: 0