JavaFx runlater method issue, when trying to access FXML generated with SceneBuilder - java

I have an issue when trying to modify a JavaFx label, created using SceneBuilder, in an asynchronous manner using runLater().
I can modify it from a callback triggered from a button, but when I try to access to the same element from the runLater method I get an error statting that this element is null.
The error code:
jun 30, 2021 3:42:25 P. M. javafx.fxml.FXMLLoader$ValueElement processValue
WARNING: Loading FXML document with JavaFX API of version 16 by JavaFX runtime of version 11.0.2
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException: Cannot invoke "javafx.scene.control.Label.setText(String)" because "this.texto" is null
at test.view.GuiApp.lambda$0(GuiApp.java:58)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:831)
My code:
public class GuiApp extends Application{
private Region rootLayout;
private Stage stage;
#FXML private Pane tankPane;
#FXML public Label texto ;
public static GuiApp main;
#Override
public void start(Stage primaryStage) throws IOException {
main = this;
stage = primaryStage;
stage.setTitle("mainStage");
stage.setResizable(false);
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(GuiApp.class.getResource("main.fxml"));
rootLayout = (Region) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
stage.setScene(scene);
stage.show();
Platform.runLater(() -> texto.setText("klfdsfklañsd"));
}
public static void main(String[] args) {
launch(args);
}
#FXML
public void updateRandom()
{
Random r = new Random();
texto.setText(Float.toString(r.nextFloat()));
}
#FXML
private void onReload() {
updateRandom();
}
}
I have tried to reduce it to the simplest, and sorry for my english by the way.

Related

How to remove flickering of main window at the beginning of a video in Javafx?

I'm making the application with JavaFX and Scene Builder. I have a fullscreen video before my appliaction start. SO it is a kind of intro for my appliaction.
A video has the same background as my main stage.
The problem is I have flickering effect at the very beginning when I launch the appliaction. Buttons and text from main window appear during like half of a second and then my video start.
How can I remove or hide that effect?
Please, check this link for more clear understanding of my problem
Video
Main Class:
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("fxml/card.fxml"));
Parent root = (Parent) loader.load();
Scene scene = new Scene(root, 1600, 600);
primaryStage.setScene(scene);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setMaximized(true);
primaryStage.setResizable(true);
primaryStage.getIcons().add(new Image("logo-icon.png"));
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Controller where I place All buttons and text. I also have a video here:
public class Controller implements Initializable {
#FXML private AnchorPane mainAnchor, anchorRow, randomCard, randomCardBack, previewCard, bolt1;
#FXML private StackPane hBoxCat0;
#FXML private Button btnPalette, btnFont, btnQuestCards, btnNonQuestCards;
#FXML private ToggleButton btnPref1, btnPref2, btnPref3, btnPref4, btnPref5, btnPref6, btnPref7, btnPref8;
#FXML private Label lb_randomCard, lb_randomCardBack, answerID, lb_previewCard, category1, category2, category3, category4, category5, category6, category7, category8;
#FXML private ToggleGroup group;
#FXML private JFXColorPicker colorPickerCategory;
#FXML private Rectangle rectRandom, rectRandomBack;
#FXML private MediaView media;
private MediaPlayer mp;
private static final String MEDIA_URL = "intro.mp4";
#FXML
public void initialize(URL location, ResourceBundle resources) {
mp = new MediaPlayer(new Media(this.getClass().getResource(MEDIA_URL).toExternalForm()));
media.setMediaPlayer(mp);
media.setSmooth(true);
mp.setAutoPlay(true);
Timeline tm = new Timeline(new KeyFrame(Duration.millis(3000), new KeyValue(media.opacityProperty(), 0.0)));
tm.setDelay(Duration.millis(5500));
tm.play();
addQuestionsToArrayCat1();
addAnswersToArrayCat1();
addSentencesToArray();
matchSentences();
}
}

JavaFX Setting SplitPane divider after init regarding components

I want to set my SplitPane divider to a "specific" starting position so, that takes into consideration the components of the window.
There is a fix starter sized TableView, but the window size can be different. So I would like to set the divider position at start so, that the table is fully visible, and right next to it stands the divider.
I have the following code so far in the public void initialize():
Controller:
#FXML
SplitPane splitPane;
#FXML
TreeTableView treeTable;
public void initialize() {
getStage().addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
double tableWidth = treeTable.getWidth();
double stageWidth = getStage().getWidth();
splitPane.setDividerPositions(tableWidth / stageWidth);
}
});
}
FXML:
<SplitPane fx:id="splitPane">
<items>
<TreeTableView fx:id="treeTable" prefWidth="280">
<!-- table -->
</TreeTableView>
<AnchorPane fx:id="anchorPane">
<!-- anchor pane -->
</AnchorPane>
</items>
</SplitPane>
But it's not working, beacuse the Stage is null at this moment.
So the problem is everything isn't loaded yet so to fix this you can call it at the end of your Main start function as below
Main:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader = new FXMLLoader(getClass().getResource("/Sample.fxml"));
Scene scene = new Scene(loader.load());
primaryStage.setScene(scene);
Controller controller = loader.getController();
controller.setStartingPosition(primaryStage);
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
}
Controller:
public class Controller {
public SplitPane splitPane;
public TreeTableView treeTable;
public AnchorPane anchorPane;
public void setStartingPosition(Stage stage){
stage.addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
double tableWidth = treeTable.getWidth();
double stageWidth = stage.getWidth();
splitPane.setDividerPositions(tableWidth / stageWidth);
}
});
}
}
I cannot tell if this works as I do not have the "other components" of which you speak so let me know if this works it looks the same both ways for me

JavaFX Updating UI from another thread

I have a main class
public class Main {
public static void main(String[] args) {
Application.launch(View.class);
View view = new View();
Platform.runLater(() -> view.changeTitle());
}
}
and a view JavaFX class
public class View extends Application {
Stage primaryStage;
public View() {
}
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
this.primaryStage = primaryStage;
primaryStage.show();
}
public void changeTitle() {
primaryStage.setTitle("YEA!");
}
}
I want the main class to do something in JavaFX thread but my code doesn't work. In documentation said that I can call Platform.runLater() from any thread I want. If I call Platform.runLater() from JavaFX thread (in start(), for example), everything is OK.
First, main(String) is blocked on Application.launch(Class).
From Oracle Javadocs:
The launch method does not return until the application has exited, either via a call to Platform.exit or all of the application windows have been closed.
Second, you're creating a new View instance on the second line. That will NOT be same instance Application created, so your Platform.runLater wouldn't affect the launched application even if that code were reachable before it exited.

How to add JavaFX Preloader ProgressBar on top of an FXML scene

I am new to Java and JavaFX all together, so please be patient with me if I am asking the wrong question here.
I am trying to add preloader Scene to an application I built: I was able to add the preloader using the code below. It's the default preloader and it looks very basic. So, here is my question. 1) how to add percentage progress status instead of progressbar. 2) Is it possible to load this progress bar on top of an FXML scene
Preloader
public class JavaFXPreloader3 extends Preloader {
ProgressBar bar;
Stage stage;
private Scene createPreloaderScene() throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("FXML.fxml"));
ProgressIndicator pi = new ProgressIndicator();
BorderPane p = new BorderPane();
p.setCenter(pi);
return new Scene(p, 300, 150);
}
#Override
public void start(Stage stage) throws Exception {
this.stage = stage;
stage.setScene(createPreloaderScene());
stage.show();
}
#Override
public void handleStateChangeNotification(StateChangeNotification scn) {
if (scn.getType() == StateChangeNotification.Type.BEFORE_START) {
stage.hide();
}
}
#Override
public void handleProgressNotification(ProgressNotification pn) {
bar.setProgress(pn.getProgress());
}
}
How do I return root scene instead of the BorderPane p including the Progress Indicator:
ProgressIndicator pi = new ProgressIndicator();
BorderPane p = new BorderPane();
p.setCenter(pi);
return new Scene(p, 300, 150);
This is a excellent tutorial for creating a PreLoader.
https://docs.oracle.com/javafx/2/deployment/preloaders.htm

Reach a variable from another class (which is controller class of JavaFX)

I'm trying to implement a game on JavaFX. Moreover, I'm dealing with an FXML file so I have a main class and controller class. My question is how can I reach the objects of the main class from the controller class. To be more clear I will share a simple code.
This is main class:
public class JavaFXApplication1 extends Application {
#Override
public void start(Stage primaryStage) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("Risk3.fxml"));
// Main Pane
BorderPane borderPane = new BorderPane();
borderPane.setCenter(root);
// Main scene
Scene scene = new Scene(borderPane);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
So for example I want to reach root or borderPane from controller class which is:
public class SampleController implements Initializable {
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
Should I make root and borderPane global and static or is there any another way to reach them ?.
The root panel can simply reached from the FXML controller using
#FXML tag like any component.
<BorderPane xmlns:fx="http://javafx.com/fxml" fx:id="root">
...
</BorderPane>

Categories