why is ImageView reference null when I get it using id? - javafx

I put an image o my fxml file, give an id to it and then use the id in relevant controller.
but I don't understand why it is null??
here is my fxml file :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.Pane?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Ball">
<children>
<ImageView fx:id="imageView" fitHeight="150.0" fitWidth="200.0" layoutX="153.0" layoutY="116.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../resources/2.jpg" />
</image>
</ImageView>
</children>
</Pane>
and this is the controller class :
package sample;
import javafx.fxml.FXML;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;
import static javafx.scene.input.KeyCode.UP;
public class Ball {
#FXML
public ImageView imageView;
public void moveBallOnKeyPress(KeyEvent e) {
if (e.getCode().equals(UP)) {
System.out.println(imageView);
}
}
}
and here is how I call this method :
package sample;
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 {
public static Scene scene ;
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World!");
scene = new Scene(root, 600, 550);
Ball ball = new Ball();
scene.setOnKeyPressed(e -> ball.moveBallOnKeyPress(e));
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
what I see in console is 'null' and calling methods on imageView I get null pointer exception

You are setting the onKeyPressed handler for the Scene to invoke a method on an instance of Ball you created:
Ball ball = new Ball();
scene.setOnKeyPressed(e -> ball.moveBallOnKeyPress(e));
#FXML-annotated fields are, of course, only initialized in the controller. They will not somehow be initialized in other objects simply because they are instances of the same class. You need to set the event handler to refer to the actual controller:
#Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root = loader.load();
primaryStage.setTitle("Hello World!");
scene = new Scene(root, 600, 550);
Ball ball = loader.getController();
scene.setOnKeyPressed(e -> ball.moveBallOnKeyPress(e));
// or scene.setOnKeyPressed(ball::moveBallOnKeyPress);
primaryStage.setScene(scene);
primaryStage.show();
}

Related

JAVAFX SplashScreen with an animated gif

Is it possible to have an animated gif or video in my splash screen.
Here is my code
Launcher class: ** EDITED With all needed files to run simple project.
package com.example;
import javafx.application.Application;
public class Launcher
{
public static void main(String[] args) {
System.setProperty("javafx.preloader", "com.example.SplashScreen");
Application.launch(Main.class);
}
}
Main class
package com.example;
import javafx.application.Application;
import javafx.application.Preloader.StateChangeNotification;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class Main extends Application{
Stage stage;
Scene scene;
#Override
public void start(Stage primaryStage) throws Exception {
try {
Thread.sleep(5000);
this.stage = primaryStage;
AnchorPane pane;
FXMLLoader mainLoader = new FXMLLoader(getClass().getClassLoader().getResource("layouts/main.fxml"));
pane = mainLoader.load();
scene = new Scene(pane, 600, 445);
primaryStage.setScene(scene);
primaryStage.show();
notifyPreloader(new StateChangeNotification(StateChangeNotification.Type.BEFORE_START));
}
catch (final Exception e) {
e.printStackTrace();
}
}
}
SplashScreenClass
package com.example;
import javafx.application.Preloader;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class SplashScreen extends Preloader {
Stage stage;
#Override
public void start(Stage stage) throws Exception {
this.stage = stage;
StackPane root = (StackPane) FXMLLoader.load(getClass().getClassLoader().getResource("layouts/splash.fxml"));
Scene scene = new Scene(root, 690, 380,true, SceneAntialiasing.BALANCED);
scene.setFill(Color.TRANSPARENT);
stage.initStyle(StageStyle.UNDECORATED);
stage.setScene(scene);
stage.show();
}
#Override
public void handleApplicationNotification(PreloaderNotification pn) {
if (pn instanceof StateChangeNotification) {
//hide after get any state update from application
stage.hide();
}
}
}
So basically this is all the java code needed. I added a sleep for 5 seconds to simulate loading from backend. This will generate a splash screen but the is not animated. If I do no hide the splashscreen stage, the gif will be animated after the main stage is loaded.
splash fxml
<StackPane xmlns="http://javafx.com/javafx/15.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="380.0" prefWidth="690.0">
<children>
<ImageView fitHeight="374.0" fitWidth="686.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../test.gif" />
</image>
</ImageView>
</children>
</StackPane>
and finally main.fxml
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" >
<children>
<Label layoutX="238.0" layoutY="224.0" prefHeight="63.0" prefWidth="125.0" text="Label" />
</children>
</AnchorPane>
Thanks to the #jewelsea comment, I ended up modifying the main class to do tasks in a background thread and then, when everything is ready, I show the stage for the main application.
[...]
try {
final Task<Integer> task = new Task<Integer>() {
#Override
protected Integer call() throws Exception {
Thread.sleep(5000);
Platform.runLater(new Runnable() {
public void run() {
showStage();
}
});
return 0;
}
};
Thread thread = new Thread(task);
thread.start();
} catch (final Exception e) {
e.printStackTrace();
}
}
public void showStage() {
stage.show();
notifyPreloader(new StateChangeNotification(StateChangeNotification.Type.BEFORE_START));
}
The GIF is animated and the splash screen will load after all background tasks are done.

JavaFX Animation(TranslateTransition) Not running but scene is being displayed without error messages

I'm worked with java for a year and am pretty new to javaFX and have been have been following a basic tutorial so far using scenebuilder. I've tried to apply a translatetransition to my button so far but it doesn't seem to move at all. When I run the program the scene with its background is displayed, but the button just stays in its defined start position in scenebuilder and won't move. After checking other similar questions on this site I've made sure that I implemented Initializable and adding #Override before my initialize function, and I've made sure my transition is played. I've tried the translatetransition on a rectangle too and it won't move. Might just be that I'm using eclipse and not netbeans
Driver Class:
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
//import javafx.scene.layout.BorderPane;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
Parent myroot = FXMLLoader.load(getClass().getClassLoader().getResource("MyFxml.fxml"));
//BorderPane root = new BorderPane();
Scene scene = new Scene(myroot);
//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);
}
}
FXML
<?import javafx.scene.shape.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<ImageView fitHeight="413.0" fitWidth="638.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../../../Downloads/campusmap.png" />
</image>
</ImageView>
<AnchorPane prefHeight="200.0" prefWidth="200.0">
<children>
<Rectangle fx:id="myrectangle" arcHeight="5.0" arcWidth="5.0" fill="#128cff" height="200.0" layoutX="14.0" layoutY="64.0" stroke="BLACK" strokeType="INSIDE" width="200.0" />
<Button fx:id="startbutton" layoutX="202.0" layoutY="232.0" mnemonicParsing="false" prefHeight="64.0" prefWidth="197.0" style="-fx-background-color: #FFC0CB; -fx-background-radius: 100;" text="Start Program">
<font>
<Font name="Comic Sans MS" size="12.0" />
</font></Button>
</children>
</AnchorPane>
</children>
</StackPane>
FXML Controller
package application;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;
import javafx.animation.TranslateTransition;
public class MyFxmlController implements Initializable{
#FXML
private Button startbutton;
#FXML
private Rectangle myrectangle;
#Override
public void initialize(URL url, ResourceBundle rb) {
TranslateTransition transition = new TranslateTransition();
transition.setDuration(Duration.seconds(4));
transition.setNode(startbutton);
transition.setToX(-200);
transition.setToY(-200);
transition.play();
}
}
You are not "linking" your controller to the FXML document. So when you display the FXML layout, the Transition code is never executed.
You have a couple of options to do this. In SceneBuilder, you can specify the controller class here:
This will add the fx:controller attribute to your FXML file:
fx:controller="temp.translate.MyFxmlController"
Obviously, you'll need to use your own package paths here.
The other option is to specify the controller in your Java code by updating your loading of the FXML document.
In order to do so, you'll need to get a reference to the FXMLLoader and set the controller there. You can change your start() method like this:
#Override
public void start(Stage primaryStage) {
try {
// Create a new FXMLLoader and set the FXML path
FXMLLoader loader = new FXMLLoader(getClass().getResource("MyFxml.fxml"));
// Set the controller for this FXML document
loader.setController(new MyFxmlController());
// Load the FXML into your Parent node
Parent myRoot = loader.load();
//BorderPane root = new BorderPane();
Scene scene = new Scene(myRoot);
//scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
Note: If you've specified the controller in your FXML via fx:controller, you cannot also specify it in your Java code, and vice versa. You may only define the controller in one place or the other, so it's really personal preference and depends on your needs.

How to create a scrollpane on fxml using controller

I would like to create a scrollpane and button from the controller and display it in fxml, is that possible? This my code but it doesn't work
controller
package application;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
public class controller {
ScrollPane scrollPane = new ScrollPane();
Button button = new Button("My Button");
public void initialize() {
button.setPrefSize(400, 300);
scrollPane.setContent(button);
scrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
scrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED);
}
}
main
package application;
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 {
final Parent parent = FXMLLoader.load(getClass().getResource("ui.fxml"));
primaryStage.setTitle("ScrollPane Demo ");
primaryStage.setScene(new Scene(parent,600, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);}
}
fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>
<VBox id="vbox" prefHeight="400" prefWidth="400.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.controller">
<children>
<Pane prefHeight="200.0" prefWidth="200.0" />
</children>
</VBox>
You need to include your vbox in your controller, like this:
#FXML
private VBox vbox;
After that, add your scrollpane to your vbox's children:
vbox.getChildren().add(scrollPane);
Note that, you not added your button to your scrollpane's content, so your scrollpane is currently empty!
Edit: why you want to add nodes via fxml and from code too? Use this solution if it is really necessary, anyway use fxml to build your UI.

JavafX - Dynamic ImageView Set Default Size

I have an ImageView which I have configured so that it is resized to fit its parent AnchorPane with the two following lines of code (both the AnchorPane and the image are square).
preview.fitWidthProperty().bind(previewPane.maxWidthProperty());
preview.fitHeightProperty().bind(previewPane.maxWidthProperty());
I have also set a fitWidth of 450 px in an fxml file that injects all nodes. When I create an instance of this object however it opens the preview with a width equal to that of the loaded image (2000 px) and not 450 px. The resizing functionality works as expected and it is possible to shrink it down to 450 px. I was wondering however if it is possible to make sure it opens up with a default size different from that of the width of the image but also making sure so that it grows and shrinks with its parent.
I have tried setting loads of different default widths in both java code and fxml but the only thing that seems to work is unbinding the fitWidth which I don't want.
Do you have any suggestions as to how I might solve this?
Main:
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class MainTest extends Application {
#FXML private AnchorPane previewPane;
#FXML private ImageView preview;
#Override
public void start(Stage primaryStage) {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Test.fxml"));
fxmlLoader.setController(this);
fxmlLoader.setClassLoader(getClass().getClassLoader());
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
preview.fitWidthProperty().bind(previewPane.maxWidthProperty());
preview.fitHeightProperty().bind(previewPane.maxWidthProperty());
Scene scene = new Scene(previewPane);
primaryStage.setTitle("Test");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane fx:id="previewPane" prefHeight="200.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<children>
<ImageView fx:id="preview" fitHeight="450.0" fitWidth="450.0" layoutX="55.0" layoutY="36.0" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<image>
<Image url="#Blue%20Eyes%20White%20Dragon.png" />
</image>
</ImageView>
</children>
</AnchorPane>
EDIT: Here is a MCVE of my problem.
The structure of your project is wrong. Also, to fix the problem you are having, you need to set the Scene size.
Code to get the ImageView from the Controller
//Code in the Main
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
Parent root = loader.load();
FXMLDocumentController fXMLDocumentController = loader.getController();
ImageView preView = fXMLDocumentController.getPreview();
//Code in the Controller
public ImageView getPreview()
{
return preview;
}
Code to set the Scene's size
stage.setWidth(450);
stage.setHeight(450);
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication213 extends Application
{
#Override
public void start(Stage stage) throws Exception
{
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
Parent root = loader.load();
FXMLDocumentController fXMLDocumentController = loader.getController();
ImageView preView = fXMLDocumentController.getPreview();
Scene scene = new Scene(root);
stage.setWidth(450);
stage.setHeight(450);
stage.setScene(scene);
stage.show();
preView.fitHeightProperty().bind(stage.heightProperty());
preView.fitWidthProperty().bind(stage.widthProperty());
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
Controller
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.image.ImageView;
/**
*
* #author blj0011
*/
public class FXMLDocumentController implements Initializable
{
#FXML
private ImageView preview;
#Override
public void initialize(URL url, ResourceBundle rb)
{
// TODO
}
public ImageView getPreview()
{
return preview;
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane fx:id="previewPane" prefHeight="200.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication213.FXMLDocumentController">
<children>
<ImageView fx:id="preview" fitHeight="450.0" fitWidth="450.0" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<image>
<Image url="#Blue%20Eyes%20White%20Dragon.png" />
</image>
</ImageView>
</children>
</AnchorPane>
Finally, you need to uncheck Preserve Ratio. The FXML should already have this unchecked. I am pointing this out because it's important.

How to update a scene without refreshing the scene after receiving data from another class/controller

I'm working on a personal weather project that contains Main.fxml, MainController.java, EnterCityDocument.fxml, and EnterCityDocumentController.java.
Main.fxml: contains a border-pane and at its center it has a ListView that displays names of cities. It also has a "Add" Button to open a new modal window(EnterCityDocument.fxml) to add a city to its ListView.
EnterCityDocument.fxml: has a listView that contains names of cities and a "Select" button to select a city the user wants to display in Main.fxml. When the user clicks the "Select" Button, the modal window (EnterCityDocument.fxml) closes and the Main.fxml continues to run.
MainController.java is the parent class of EnterCityDocumentController.java.
I've been looking for passing data from the child class(EnterCityDocumentController.java) to the parent class (MainController.java) and found a way to do it, but all of the methods i've found require to refresh the MainController.java class whenever the user selects a city in EnterCityDocumentController.java.
Is there a way to update the Main.fxml without refreshing the main scene when a new city is added into the listView of the Main.fxml?
Hope my question is clear enough. If you need a further explanation/code, please let me know!
All you need to do is arrange for the new city from the dialog to be added to the list view's backing list. Since you are using showAndWait() this is very easy: just define a method in the EnterCityDocumentController class that returns the new city, and call it after showAndWait():
public class EnterCityDocumentController {
#FXML
private TextField cityNameField ;
// other fields, etc...
#FXML
private void okButtonPressed() {
// just close the window:
cityNameField.getScene().getWindow().hide();
}
public City getUserCity() {
return new City(cityNameField.getText());
}
}
Then in the main controller:
public class MainController {
#FXML
private ListView<City> listView ;
// handler method:
#FXML
public void addNewCity() throws IOException {
FXMLLoader loader = new FXMLLoader(EnterCityDocumentController.class.getResource("EnterCityDocument.fxml"));
Scene scene = new Scene(loader.load());
Stage stage = new Stage();
stage.setScene(scene);
stage.showAndWait();
listView.getItems().add(controller.getUserCity());
}
}
Here is a SSCCE:
app/Main.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.MainController">
<center>
<ListView fx:id="listView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
</center>
<bottom>
<Button mnemonicParsing="false" onAction="#addNewElement" text="Add..." BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
</BorderPane.margin>
</Button>
</bottom>
</BorderPane>
app/AddNewElement.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane hgap="5.0" vgap="5.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.AddNewElementController">
<rowConstraints>
<RowConstraints />
<RowConstraints />
</rowConstraints>
<columnConstraints>
<ColumnConstraints halignment="CENTER" />
<ColumnConstraints halignment="CENTER" />
</columnConstraints>
<children>
<TextField fx:id="textField" GridPane.columnSpan="2" />
<Button defaultButton="true" mnemonicParsing="false" onAction="#ok" text="OK" GridPane.rowIndex="1" />
<Button cancelButton="true" mnemonicParsing="false" onAction="#cancel" text="Cancel" GridPane.columnIndex="1" GridPane.rowIndex="1" />
</children>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
</GridPane>
app/AddNewElementController.java:
package app;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
public class AddNewElementController {
#FXML
private TextField textField ;
private boolean approved ;
public boolean isApproved() {
return approved ;
}
public String getUserText() {
return isApproved() ? textField.getText() : null ;
}
#FXML
private void cancel() {
approved = false ;
hide();
}
#FXML
private void ok() {
approved = true ;
hide();
}
private void hide() {
textField.getScene().getWindow().hide();
}
}
app/MainController.java:
package app;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class MainController {
#FXML
private ListView<String> listView ;
#FXML
private void addNewElement() throws IOException {
FXMLLoader loader = new FXMLLoader(AddNewElementController.class.getResource("AddNewElement.fxml"));
Parent root = loader.load();
AddNewElementController controller = loader.getController();
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
stage.initOwner(listView.getScene().getWindow());
stage.setScene(scene);
stage.showAndWait();
if (controller.isApproved()) {
listView.getItems().add(controller.getUserText());
}
}
}
app/Main.java:
package app;
import java.io.IOException;
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 IOException {
FXMLLoader loader = new FXMLLoader(MainController.class.getResource("Main.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Another, very slightly different approach, is to pass the listview's backing list to the second controller. Then the second controller can add the item directly to the list (and the list view will automatically update, as before). This approach can be used even if you are not using showAndWait() (it doesn't rely on the code blocking until the user dismisses the window).
package app;
import java.util.List;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
public class AddNewElementController {
#FXML
private TextField textField ;
private List<String> itemList ;
public void setItemList(List<String> itemList) {
this.itemList = itemList ;
}
#FXML
private void cancel() {
hide();
}
#FXML
private void ok() {
itemList.add(textField.getText());
hide();
}
private void hide() {
textField.getScene().getWindow().hide();
}
}
and
package app;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class MainController {
#FXML
private ListView<String> listView ;
#FXML
private void addNewElement() throws IOException {
FXMLLoader loader = new FXMLLoader(AddNewElementController.class.getResource("AddNewElement.fxml"));
Parent root = loader.load();
AddNewElementController controller = loader.getController();
controller.setItemList(listView.getItems());
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
stage.initOwner(listView.getScene().getWindow());
stage.setScene(scene);
stage.showAndWait();
}
}

Resources