I know how to parse variables to controllers in JavaFX with fxml. But i need to use them in the initialize method of my controller. Is there a ways to do this? The background is, that i have a interface, where you can define different settings. Now you can safe them and have to be able to reopen them. So now when i open a rule, i need to set the values in the new option view. I know, that it works on text fields (UI-Elements) to set Text during initialize but not for variables. I tried different approaches. Like binding with properties (works for visibility property of button (UI-Element) but not for variables to set. Do you know a way or maybe an other approach?
Here is my example:
Controller1:
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Stage;
/**
*
* #author Sandro
*/
public class FXMLDocumentController implements Initializable {
#FXML
private Button btn_openWindow;
#FXML
private void handleButtonAction(ActionEvent event) {
try {
Stage stage = new Stage();
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(getClass().getResource("fxml_second.fxml").openStream());
Fxml_secondController cont = (Fxml_secondController)loader.getController();
cont.setFlag(0x00000002);
cont.setIsChange(false);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
} catch (IOException ex) {
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
public void initialize(URL url, ResourceBundle rb) {
btn_openWindow.setOnAction(this::handleButtonAction);
}
Controller 2:
/**
* FXML Controller class
*
* #author Sandro
*/
public class Fxml_secondController implements Initializable {
#FXML private Button btn_printOut;
private boolean isChange = true;
private int flag = 0x00000001;
private void printOut(ActionEvent event){
System.out.println("isChange: "+isChange);
System.out.println("flag: "+flag);
}
public boolean isIsChange() {
return isChange;
}
public void setIsChange(boolean isChange) {
this.isChange = isChange;
}
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
this.flag = flag;
}
#Override
public void initialize(URL url, ResourceBundle rb) {
btn_printOut.setOnAction(this::printOut);
System.out.println(flag);
}
In controller 2 you see the problem. The console-output in initialize shows 1 but it need to show 2. If i klick on printOut (Button) it prints out the right values which i have set in Controller 1.
Set the controller in the Java code, instead of in FXML.
Remove the fx:controller attribute from fxml_second.fxml, and change the code in FXMLDocumentController as follows:
#FXML
private void handleButtonAction(ActionEvent event) {
try {
Stage stage = new Stage();
FXMLLoader loader = new FXMLLoader(getClass().getResource("fxml_second.fxml"));
Fxml_secondController cont = new Fxml_secondController();
cont.setFlag(0x00000002);
cont.setIsChange(false);
loader.setController(cont);
Parent root = loader.load();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
} catch (IOException ex) {
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
}
Another option would be to use a custom component approach for the second fxml.
Related
I have desktop app with side menu bar. Main window is BorderPane with InsertLeft containing VBox. I set Hbox buttons and their behaviour then I add them one by one to the VBox. InsertCenter has just Pane with alot of elements.
I've created 3 fxml files for each GUI layout.
sample.fxml - BorderPane: InsertLeft->Menu(VBox), InsertCenter->Empty Pane
tab1_content.fxml - Pane filled with ProgressBar, Labels and Buttons
tab2_content.fxml - Not yet implemented (Empty Pane)
Each of these fxml files has their controllers.
I would like to switch content of borderPane.center() inside sample.fxml on menu button click.
I've managed to fix some issues, but main problem is with loading data into .fxml views.
As I run my App it works perfectly, each fxml file has his FXMLLoader which will load content into borderPane right inside main Controller.
Problem occurs while I click on Buttons. It will switch panes, but actual content will reset to default state and Main.class initialization is completely ignored. Button listeners and label values are not initialized. It's just empty fxml layout. Every variable inside Main.class, what I want to access from Tab1Controller is returning NullPointerException - even methods.
Each controller extends AbstractController, which contains Main.class instance which is initialized inside start() method. So i should be able to have access to every Main.class method/variable at any time.
Issue Gif:
Some code samples:
My Main.class start() method:
public Controller myController;
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/sample.fxml"));
myController = new Controller();
myController.setMainApp(this);
loader.setController(myController);
Parent root = loader.load();
primaryStage.setTitle("Simple App");
primaryStage.setScene(new Scene(root));
primaryStage.show();
<other stuff>
}
public void setDefaultViewProperties(){
currentScanningFileProperty = new SimpleStringProperty();
myController.tab1Controller.actualPath.textProperty().bind(currentScanningFileProperty); //NullPointerException while called from Controller
fileCounterProperty = new SimpleLongProperty();
myController.tab1Controller.numOfScanned.textProperty().bind(fileCounterProperty.asString());
maliciousFilesCounterProperty = new SimpleIntegerProperty();
myController.tab1Controller.numOfMaliciousFiles.textProperty().bind(maliciousFilesCounterProperty.asString());
myController.tab1Controller.fileChoiceBtn.setOnMouseClicked(event -> chooseFile());
myController.tab1Controller.runScanBtn.setOnMouseClicked(event -> new Thread(() -> {
try {
resetValues();
startFileWalking(chosenFile);
} catch (IOException e) {
e.printStackTrace();
}
}).start());
}
MainController:
package sample;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller extends AbstractController implements Initializable{
public HBox sideMenu;
public VBox mainMenu;
public BorderPane borderPane;
public Boolean isButton1Pressed = false;
public Boolean isButton2Pressed = false;
public static final String TAB_1 = "TAB-1";
public static final String TAB_2 = "TAB-2";
public Button malwareButton;
public Button webShieldButton;
public Tab1Controller tab1Controller;
public Tab2Controller tab2Controller;
#Override
public void initialize(URL location, ResourceBundle resources) {
createMenuButtons();
setSideMenu();
setMenuButtonsListeners();
}
private void setSideMenu(){
mainMenu.getChildren().add(item(malwareButton));
mainMenu.getChildren().add(item(webShieldButton));
mainMenu.setStyle("-fx-background-color:#004D40");
}
private HBox item(Button menuButton){
menuButton.setPrefSize(200, 50);
menuButton.setStyle("-fx-background-color: transparent;");
menuButton.setTextFill(Color.web("#E0F2F1"));
menuButton.setPadding(Insets.EMPTY);
sideMenu = new HBox(menuButton);
return sideMenu;
}
public void setMenuButtonsListeners(){
malwareButton.setOnMousePressed(event -> {
setButtonStylePressed(malwareButton);
setButtonStyleUnpressed(webShieldButton);
isButton1Pressed = true;
isButton2Pressed = false;
loadTab1Content();
main.setDefaultViewProperties();
});
webShieldButton.setOnMousePressed(event -> {
setButtonStylePressed(webShieldButton);
setButtonStyleUnpressed(malwareButton);
isButton1Pressed = false;
isButton2Pressed = true;
loadTab2Content();
});
malwareButton.setOnMouseExited(event -> {
if(!isButton1Pressed){
setButtonStyleUnpressed(malwareButton);
}
});
webShieldButton.setOnMouseExited(event -> {
if(!isButton2Pressed){
setButtonStyleUnpressed(webShieldButton);
}
});
malwareButton.setOnMouseEntered(event -> setButtonStylePressed(malwareButton));
webShieldButton.setOnMouseEntered(event -> setButtonStylePressed(webShieldButton));
}
public void setButtonStylePressed(Button btn){
btn.setStyle("-fx-background-color: #E0F2F1");
btn.setTextFill(Color.web("#004D40"));
}
public void setButtonStyleUnpressed(Button btn){
btn.setStyle("-fx-background-color: transparent");
btn.setTextFill(Color.web("#E0F2F1"));
}
private void loadTab1Content(){
FXMLLoader tab1loader = new FXMLLoader();
tab1loader.setLocation(getClass().getResource("/tab_1_content.fxml"));
try {
if (tab1Controller == null){
tab1Controller = new Tab1Controller();
}
tab1loader.setController(tab1Controller);
borderPane.setCenter(tab1loader.load());
} catch (IOException e) {
e.printStackTrace();
}
}
private void loadTab2Content(){
FXMLLoader tab2loader = new FXMLLoader();
tab2loader.setLocation(getClass().getResource("/tab_2_content.fxml"));
try {
if (tab2Controller == null){
tab2Controller = new Tab2Controller();
}
tab2loader.setController(tab2Controller);
borderPane.setCenter(tab2loader.load());
} catch (IOException e) {
e.printStackTrace();
}
}
private void createMenuButtons(){
malwareButton = new Button();
malwareButton.setText(TAB_1);
webShieldButton = new Button();
webShieldButton.setText(TAB_2);
}
}
Tab1Controller:
package sample;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.ResourceBundle;
/**
* Created by admin on 5. 5. 2018.
*/
public class Tab1Controller extends AbstractController implements Initializable {
public ProgressBar progressBar;
public Button runScanBtn;
public Button fileChoiceBtn;
public Label chosenPath;
public Label actualPath;
public Label numOfMaliciousFiles;
public Label hashValue;
public Label scanFinishedMsg;
public Label numOfScanned;
public Button showFoundMalwareButton;
#Override
public void initialize(URL location, ResourceBundle resources) {
runScanBtn.setDisable(true);
scanFinishedMsg.setVisible(false);
showFoundMalwareButton.setVisible(false);
showFoundMalwareButton.setOnAction(event -> showPopupWindow());
}
Update#1 - Updating fxml values through Main.class after button click
I've finally managed to run app without exception. I had to create next Controller for pane fxml layout itself called Tab1Controller. When I initialized Controller, it instantly initialized Tab1Controller inside. So when I want to change Center BorderPane label i had to call myController.tab1Controller.tabLabel.setText()
I don't know if it's good approach to this problem.
But now I'm back to my old problem. When I click on TAB-1 it will load content, but values are not initialized to default state.
For example I have couple of labels updated in real time. I binded some SimpleProperties into it with default values. It worked before, but as I have three controllers it will load data for a first time, but when I click TAB-1 button it will load just fxml content, but it will not set those labels.
So i made public method inside Main.class which I will call everytime I switch to TAB-1 from Controller.
public void setDefaultViewProperties(){
myController.tab1Controller.actualPath.textProperty().bind(currentScanningFileProperty);
myController.tab1Controller.numOfScanned.textProperty().bind(fileCounterProperty.asString());
myController.tab1Controller.numOfMaliciousFiles.textProperty().bind(maliciousFilesCounterProperty.asString());
}
But now everytime I click on TAB-1 I've got
java.lang.NullPointerException: Cannot bind to null
You can make two pane and switch between them using setVisible() method
example:
void btn1Clicked() {
pane1.setVisible(true);
pane2.setVisible(false);
}
void btn2Clicked() {
pane1.setVisible(false);
pane2.setVisible(true);
}
You could use a TabPane to achieve this behaviour:
https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TabPane.html
Solved. I'm not sure how, but setDefaultViewProperties() are not throwing NullPointerException at the moment. I did not change anything inside the code:
malwareButton.setOnMousePressed(event -> {
setButtonStylePressed(malwareButton);
setButtonStyleUnpressed(webShieldButton);
isButton1Pressed = true;
isButton2Pressed = false;
loadTab1Content();
main.setDefaultViewProperties();
});
I'm new to JavaFX and trying build a simple client application that has a root landing stage (rootLayout) with buttons that will allow me to swap out the RootLayout.FXML with a sub FXML. My issue begins when I attempt to load a TreeView within the sub FXML pane. The TreeView is used to load a file directory within this area of the sub-pane.
Thank you!!!
Main
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
public class MainApp extends Application {
public static Properties configProp;
private Stage primaryStage;
private static BorderPane rootLayout;
/**
* Just a root getter for the controller to use
*/
public static BorderPane getRoot() {
return rootLayout;
}
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
initRootLayout();
}
public static void main(String[] args) {
configProp = loadProperties();
launch(args);
}
/***
* Initialize the root layout
* #param args
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
RootLayoutController
import java.io.IOException;
import einMyQueue.MainApp;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Alert;
import javafx.scene.control.SplitPane;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.BorderPane;
public class RootLayoutController {
// Reference to the main application
private MainApp mainApp;
/**
* Is called by the main application to give a reference back to itself.
*
* #param mainApp
*/
public void setMainApp(MainApp mainApp) {
this.mainApp = mainApp;
}
/**
* Selects the FAST Scene
*/
#FXML
private void handleFASTSceneSelection() {
try {
SplitPane paneOne = FXMLLoader.load(getClass().getResource("FASTMessageRequest.fxml"));
BorderPane border = MainApp.getRoot();
border.setCenter(paneOne);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Sub-Controller that implements TextView
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import einMyQueue.MainApp;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.stage.DirectoryChooser;
public class FASTMessageRequestController {
private MainApp mainApp;
// FXML Annotations
#FXML private TreeView<String> msgDirTreeView;
#FXML private TextArea msgBodyTextArea;
#FXML private TextArea consoleTextArea;
public void initialize() {
// Using a Tree View
msgDirTreeView = new TreeView<String>();
DirectoryChooser dirChooser = new DirectoryChooser();
URL url = getClass().getResource("../resources/templates");
try {
dirChooser.setInitialDirectory(new File(url.toURI()));
File file = dirChooser.getInitialDirectory();
msgDirTreeView.setRoot(getNodesForDirectory(file));
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public TreeItem<String> getNodesForDirectory(File directory) {
TreeItem<String> msgDirRoot = new TreeItem<String>(directory.getName());
for(File iFile : directory.listFiles()) {
System.out.println("Loading " + iFile.getName());
if(iFile.isDirectory()) {
msgDirRoot.getChildren().add(getNodesForDirectory(iFile));
} else {
msgDirRoot.getChildren().add(new TreeItem<String>(iFile.getName()));
}
}
return msgDirRoot;
}
/***
* Name: setMainApp
* Purpose: Is called by the main application to give a reference back to itself.
*
* #param mainApp
*/
public void setMainApp(MainApp mainApp) {
// Is called by the main application to give a reference back to itself.
this.mainApp = mainApp;
}
}
I have following code:
package pl.javastart.youtufy.controller;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.web.WebEngine;
import javafx.stage.Stage;
public class ConnectionErrorController implements Initializable {
#FXML
private Label infoLabel;
#FXML
private Button tryButton;
#FXML
private Button exitButton;
#Override
public void initialize(URL location, ResourceBundle resources) {
MainController mc = new MainController();
infoLabel.setText("Connection lost, please try again");
tryButton.setText("try again");
exitButton.setText("exit");
tryButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
WebEngine webEngine = mc.getContentPaneController().getVideoWebView().getEngine(); // 1
ToggleButton playButton = mc.getControlPaneController().getPlayButton(); // 2
Node source = (Node) event.getSource();
Stage stage = (Stage) source.getScene().getWindow();
if (mc.testInet()) {
stage.close();
mc.play(webEngine, playButton);
} else {
stage.close();
MainController.exist = false;
}
}
});
exitButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Platform.exit();
}
});
}
}
I am trying to use WebEngine and ToggleButton Objects from controllers in MainController (I generated getters and setters to them in MainController):
public class MainController implements Initializable {
#FXML
private ContentPaneController contentPaneController;
#FXML
private ControlPaneController controlPaneController;
#FXML
private MenuPaneController menuPaneController;
#FXML
private SearchPaneController searchPaneController;
private Youtube youtubeInstance;
public static boolean exist = false;
public ControlPaneController getControlPaneController() {
return controlPaneController;
}
public void setControlPaneController(ControlPaneController controlPaneController) {
this.controlPaneController = controlPaneController;
}
public ContentPaneController getContentPaneController() {
return contentPaneController;
}
public void setContentPaneController(ContentPaneController contentPaneController) {
this.contentPaneController = contentPaneController;
}
But its still returns NullPointerException. I had same problem, when I tried simply make references to the controllers in ConnectionErrorController. How to refer to the ToggleButton i WebEngine Objects from controllers in ConnectionErrorController properly?
Regards
You are creating a controller instance "by hand" with
MainController mc = new MainController();
#FXML-annotated fields are initialized by the FXMLLoader when it creates the controller for you as part of the process of loading the FXML file. Since the controller you created is not the controller instance created by the FXMLLoader, its #FXML-annotated fields are uninitialized (i.e. they are null), and hence you get a null pointer exception.
You can get a reference to the controller created by the FXMLLoader by calling getController() on the FXMLLoader instance after calling load().
If you want one controller to communicate with another, then pass the reference to one controller to the other controller, by defining appropriate methods in the second controller:
public class ConnectionErrorController implements Initializable {
private MainController mainController ;
public void setMainController(MainController mainController) {
this.mainController = mainController ;
}
// ...
#Override
public void initialize(URL location, ResourceBundle resources) {
infoLabel.setText("Connection lost, please try again");
tryButton.setText("try again");
exitButton.setText("exit");
tryButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
WebEngine webEngine = mainController.getContentPaneController().getVideoWebView().getEngine(); // 1
ToggleButton playButton = mainController.getControlPaneController().getPlayButton(); // 2
if (mainController.testInet()) {
mainController.play(webEngine, playButton);
} else {
// obviously you can now do something better than the "public static field hack" here:
MainController.exist = false;
}
tryButton.getScene().getWindow().hide();
}
});
// ...
}
}
Assuming you are loading the second fxml file in a method in MainController, you can then just do something like:
public class MainController {
// ...
public void showConnectionErrorWindow(String fileName) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/ConnectionError.fxml"));
Parent root = loader.load();
ConnectionErrorController connectionErrorController = loader.getController();
connectionErrorController.setMainController(this);
Scene scene = new Scene(root);
Stage stage = new Stage();
// etc...
}
// ...
}
Note that there are much more elegant ways of solving this problem, such as passing the ConnectionErrorController a callback function (in the form of a lambda expression) to process the call to play(...), which avoid the tight coupling between the ConnectionErrorController and the MainController. However, as you seem to be new to Java, this simpler approach might be more suitable.
See Passing Parameters JavaFX FXML for more information on passing values to controllers.
I have following code:
package pl.javastart.youtufy.controller;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.web.WebEngine;
import javafx.stage.Stage;
public class ConnectionErrorController implements Initializable {
#FXML
private Label infoLabel;
#FXML
private Button tryButton;
#FXML
private Button exitButton;
#Override
public void initialize(URL location, ResourceBundle resources) {
MainController mc = new MainController();
infoLabel.setText("Connection lost, please try again");
tryButton.setText("try again");
exitButton.setText("exit");
tryButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
WebEngine webEngine = mc.getContentPaneController().getVideoWebView().getEngine(); // 1
ToggleButton playButton = mc.getControlPaneController().getPlayButton(); // 2
Node source = (Node) event.getSource();
Stage stage = (Stage) source.getScene().getWindow();
if (mc.testInet()) {
stage.close();
mc.play(webEngine, playButton);
} else {
stage.close();
MainController.exist = false;
}
}
});
exitButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Platform.exit();
}
});
}
}
I am trying to use WebEngine and ToggleButton Objects from controllers in MainController (I generated getters and setters to them in MainController):
public class MainController implements Initializable {
#FXML
private ContentPaneController contentPaneController;
#FXML
private ControlPaneController controlPaneController;
#FXML
private MenuPaneController menuPaneController;
#FXML
private SearchPaneController searchPaneController;
private Youtube youtubeInstance;
public static boolean exist = false;
public ControlPaneController getControlPaneController() {
return controlPaneController;
}
public void setControlPaneController(ControlPaneController controlPaneController) {
this.controlPaneController = controlPaneController;
}
public ContentPaneController getContentPaneController() {
return contentPaneController;
}
public void setContentPaneController(ContentPaneController contentPaneController) {
this.contentPaneController = contentPaneController;
}
But its still returns NullPointerException. I had same problem, when I tried simply make references to the controllers in ConnectionErrorController. How to refer to the ToggleButton i WebEngine Objects from controllers in ConnectionErrorController properly?
Regards
You are creating a controller instance "by hand" with
MainController mc = new MainController();
#FXML-annotated fields are initialized by the FXMLLoader when it creates the controller for you as part of the process of loading the FXML file. Since the controller you created is not the controller instance created by the FXMLLoader, its #FXML-annotated fields are uninitialized (i.e. they are null), and hence you get a null pointer exception.
You can get a reference to the controller created by the FXMLLoader by calling getController() on the FXMLLoader instance after calling load().
If you want one controller to communicate with another, then pass the reference to one controller to the other controller, by defining appropriate methods in the second controller:
public class ConnectionErrorController implements Initializable {
private MainController mainController ;
public void setMainController(MainController mainController) {
this.mainController = mainController ;
}
// ...
#Override
public void initialize(URL location, ResourceBundle resources) {
infoLabel.setText("Connection lost, please try again");
tryButton.setText("try again");
exitButton.setText("exit");
tryButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
WebEngine webEngine = mainController.getContentPaneController().getVideoWebView().getEngine(); // 1
ToggleButton playButton = mainController.getControlPaneController().getPlayButton(); // 2
if (mainController.testInet()) {
mainController.play(webEngine, playButton);
} else {
// obviously you can now do something better than the "public static field hack" here:
MainController.exist = false;
}
tryButton.getScene().getWindow().hide();
}
});
// ...
}
}
Assuming you are loading the second fxml file in a method in MainController, you can then just do something like:
public class MainController {
// ...
public void showConnectionErrorWindow(String fileName) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/ConnectionError.fxml"));
Parent root = loader.load();
ConnectionErrorController connectionErrorController = loader.getController();
connectionErrorController.setMainController(this);
Scene scene = new Scene(root);
Stage stage = new Stage();
// etc...
}
// ...
}
Note that there are much more elegant ways of solving this problem, such as passing the ConnectionErrorController a callback function (in the form of a lambda expression) to process the call to play(...), which avoid the tight coupling between the ConnectionErrorController and the MainController. However, as you seem to be new to Java, this simpler approach might be more suitable.
See Passing Parameters JavaFX FXML for more information on passing values to controllers.
I'm writing an music player and I can't link my Observable Array with TableView defined in controller. When project runned, I get NullPointerException on line under comment. I don't know what is making it happen and how find way around this problem.
Main.java
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.SplitPane;
import javafx.stage.Stage;
public class Main extends Application {
ObservableList<CModel> modelData = FXCollections.observableArrayList();
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader();
SplitPane root = FXMLLoader.load(getClass().getResource("Player.fxml"));
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
PlayerController controller = loader.getController();
//this line gives exception
controller.setData(this);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public ObservableList<CModel> getPersonData() {
return modelData;
}
public static void main(String[] args) {
launch(args);
}
}
PlayerController.java
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Menu;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
public class PlayerController {
#FXML
private TableView<CModel> cTable;
#FXML
private TableColumn<CModel, String> artCol;
#FXML
private TableColumn<CModel, String> albCol;
#FXML
private TableColumn<CModel, String> trNameCol;
#FXML
private TableColumn<CModel, String> trNoCol;
#FXML
private TableColumn<CModel, String> durCol;
#FXML
private Menu addFile;
#FXML
private Menu addFolder;
#FXML
private Menu savePlayL;
#FXML
private Menu loadPlayL;
private Files files;
private Main main;
public PlayerController() {
}
#FXML
private void initialize() {
artCol.setCellValueFactory(
new PropertyValueFactory<CModel,String>("artistName")
);
albCol.setCellValueFactory(
new PropertyValueFactory<CModel,String>("albumName")
);
trNameCol.setCellValueFactory(
new PropertyValueFactory<CModel,String>("trackName")
);
trNoCol.setCellValueFactory(
new PropertyValueFactory<CModel,String>("trackDurationName")
);
durCol.setCellValueFactory(
new PropertyValueFactory<CModel,String>("trackNumberName")
);
}
#FXML
protected void handleAddFile(ActionEvent event) {
files = new Files();
files.openFile();
}
public void setData(Main mainApp) {
this.main = mainApp;
cTable.setItems(main.getPersonData());
}
}
You need to call loader.load() method of instantiated object, instead of using static method, before getting controller, take a look here for details.
There are few things to be considered here. In order to get the controller, you need to remember the following things :
Use the FXMLoader reference to load the FXML
Call the non-static load method. Inorder to do that pass a InputStream as a parameter.
Inside your Main.Java, you should have :
FXMLLoader loader = new FXMLLoader();
SplitPane root = (SplitPane)loader.load(getClass().getResource("Player.fxml").openStream());
If you don't want to use InputStream, you can also pass the resource in the constructor and call the non-staic load() method
FXMLLoader loader = new FXMLLoader(getClass().getResource("Player.fxml"));
SplitPane root = (SplitPane)loader.load();