JavaFX child AnchorPane not showing after adding to parent AnchorPane - java

So I am working on building an application that is kind of like an IDE, think Eclipse or Netbeans. I want to have different zones where users can select something like a view that will show up in one of the zones. My first attempt is to add a console view to on of my zones. In my application class I create the overall UI with the following code:
public void initialize() {
try {
mainController = (MainController) replaceSceneContent("/layout/main.fxml");
}catch (Exception ex) {
Logger.getLogger(MyApplication.class.getName()).log(Level.SEVERE, null, ex);
}
}
private Initializable replaceSceneContent(String fxml) throws Exception {
FXMLLoader loader = new FXMLLoader();
InputStream in = MyApplication.class.getResourceAsStream(fxml);
loader.setBuilderFactory(new JavaFXBuilderFactory());
loader.setLocation(MyApplication.class.getResource(fxml));
AnchorPane page;
try {
page = (AnchorPane) loader.load(in);
} finally {
in.close();
}
Scene scene = new Scene(page);
stage.setScene(scene);
stage.sizeToScene();
return (Initializable) loader.getController();
}
Which results in the following UI
I would then like to add a console widget/view to the southEast quadrant of the application. This will be handled by the MainController. In theory if someone chooses the Console view it will popup in one of the quadrants, but for now I just want to load it at start up. I try to do this with the following code:
public class MainController extends AnchorPane implements Initializable{
#FXML
SplitPane west, east;
#FXML
AnchorPane northWest, southWest, northEast, southEast, applicationHeader;
#FXML
MenuBar menuBar;
Stage stage;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
try {
ConsoleWidget cw = (ConsoleWidget)loadWidget("/widget/console.fxml");
southEast.getChildren().add(cw);
} catch (Exception e) {
e.printStackTrace();
}
}
private Initializable loadWidget(String fxml) throws Exception {
FXMLLoader loader = new FXMLLoader();
InputStream in = MainController.class.getResourceAsStream(fxml);
loader.setBuilderFactory(new JavaFXBuilderFactory());
loader.setLocation(MainController.class.getResource(fxml));
AnchorPane page;
try {
page = (AnchorPane) loader.load(in);
} finally {
in.close();
}
return (Initializable) loader.getController();
}
}
This should result in the following console appearing in the south east quadrant.
Programatically this seems to work. My southEast anchor pane shows that it has 1 child in its list if you look at it, however my UI doesnt change. I need to know how to refresh my Main controller (or do whatever I need to do) to make the change show up to the user.
I have created applications before where I create an entire new scene as the user goes through different pages but I have never really done something like this where I need to update the scene after adding a node to a pane. I have tried things like stage.sizeToScene() to try to refresh it but that hasnt worked for me.

You must return AnchorPane page instead of (Initializable) loader.getController(); to be added to the southEast anchorPane.
Declaring a Controller which extends another Node, results in a controller-node, which are used for creating Custom Controls.
These controls have constructors to set the Controller and the Root node. See this example
In your case, loader.getController() returns you an AnchorPane(rather ConsoleWidget), which is not loaded from the FXML, but is an instance of the ConsoleWidget class.

Related

How do you change the text in a button from another window (JavaFX + FXML)?

I am very new to using JavaFX and have having some trouble using JavaFX with FXML. I am creating a program with a "Setup" button that when clicked, opens a new window with the connection (to an Arduino) settings. On the menu is another button ("Connect") that connects to the board. This closes the window. I'm looking to have this change the text of the original "setup" button to "disconnect", however, I don't seem to be able to access the button from the "setup window". Whenever I click "connect" I get the following error:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
I read online that this is a wrapper for a null pointer exception. I assume that the "setup" button is null and that's why I can't change it, but I can't work out why.
Here is an excerpt from MainController.java:
#FXML
protected void setUpConnection(ActionEvent e) {
SetupController setupController = new SetupController();
setupController.init(this);
}
The above method gets called when the "setup" button is clicked (set in the file: setupMenu.fxml). This then opens up the separate window. Here is the code in SetupController.java that opens the window:
private void openSetupWindow() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("setupMenu.fxml"));
Parent root1 = (Parent)loader.load();
Stage stage = new Stage();
stage.setTitle("Setup Connection");
stage.setScene(new Scene(root1));
stage.show();
} catch(Exception exc) {
exc.printStackTrace();
}
}
When the connect button is clicked, the following method (in SetupController.java) is called:
private void changeButtonText(ConnectionEventType e) {
Button b = main.getSetupButton();
if(e == ConnectionEventType.CONNECT) {
b.setText("Disconnect");
}
else {
b.setText("Setup Connection...");
}
}
(main is the MainController object that was passed in to setupController.init() )
The above code is where I am getting the error.
Just to clarify, I have 2 separate fxml files, one for the main window and one for the pop up. sample.fxml(the main window) has its controller set to MainController and is set up in Main.java (below):
#Override
public void start(Stage primaryStage) throws Exception{
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
GridPane root = loader.load();
Scene scene = new Scene(root, 1200, 900);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setTitle("Nest Control");
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
Am I trying to access the button incorrectly? Is anyone able to help? Like I said, I don't have much experience with using JavaFX or FXML.
I think the answer you are looking for is to store the controllers for each window you open so you can access the variables within the controllers, however without the rest of your code would be hard to advise you, but heres an example of what i mean:
private SetupController yourController;
#Override
public void start(Stage primaryStage) throws Exception{
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
GridPane root = loader.load();
this.yourController=loader.<SetupController>getController();
Scene scene = new Scene(root, 1200, 900);
}
}
You could then pass the variable yourController to other instances in a Model-view-controller type way and access its methods.
or something like this in your case :
private void changeButtonText(ConnectionEventType e) {
Button b = this.yourController.getButton(); //a method that returns your #FXML button object in your controller
if(e == ConnectionEventType.CONNECT) {
b.setText("Disconnect");
}
else {
b.setText("Setup Connection...");
}
}
Or alternatively have a specific method within the controller that will set the text of the button without having to return the button object.
See the examples here and here
However please note the error you get seems to typically attributed to missing #FXML annotations so maybe make sure in this instance that you have annotated all the variables in any controllers also. See here for more details.

I can't add items to either ComboBox or ChoiceBox when adding another stage

So, I have two .fxml files, the other one working as main and the other one as basically window that pops up.
#FXML private void handleSelection() throws Error {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("StudentChoose.fxml"));
Parent root1 = (Parent) fxmlLoader.load();
Stage stage = new Stage(StageStyle.DECORATED);
stage.setScene(new Scene(root1));
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
sBox.setItems(FXCollections.observableArrayList("New Document", "Open ", "Save", "Save as"));
}
This is what I have. I have stated the sBox above with
#FXML private ChoiceBox sBox;
Everything works PERFECTLY when the choicebox/combobox is on the main stage, but when it is on the other stage - it doesn't. I have a .fxml file for this another stage with the content, id is set as 'sBox' for the choicebox - but it doesn't work. Instead it returns me a java.lang.NullPointerException on the line where I call to add items to it. What could be the problem?

javafx - init tabs (of tab pane) on startup

I'm using eclipse & scene builder.
I has defined the main window as follow:
I want to define the "Outgoing Messages" tab with X (X is calculated on run time) of elements (the elements defined at different fxml file).
When I'm creating the main window:
private Stage primaryStage;
private AnchorPane rootLayout;
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("ABC");
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("MainLayout.fxml"));
rootLayout = (AnchorPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
How can I update the "Outgoing Messages" tabs as I described above ?
Thanks
You may use the <fx:include> inside your FXML for Outgoing Messages. This will automatically load X number of fxml before loading the main FXML. After all the incude FXML's are loaded, the FXML for Outgoing Messages is prepared and shown.
Update
If you have dynamic increase/decrease in elemtents, use the initialize() of the Outgoing Messages to populate the elements inside it instead of using <fx:incude>.
Lets consider (you may have your own logic) your Outgoing Messages has a VBox which is to be filled with dynamic no of elements(BorderPane in this case) which is inside X.fxml (You may have different FXML or you can create you own control at runtime and add it) :
pubic class OutgoingMessagesController implements Initializable {
#FXML
private VBox vbox;
public void initialize(java.net.URL location, java.util.ResourceBundle resources) {
//Whatever your logic is, I am considering a dynamic number here
for(int i=0; i<dynamicNumber; i++){
BorderPane borderPane = FXMLoader.load(getClass().getResource("X.fxml"));
vbox.getChildren.add(borderPane);
}
}
}

Same Stage, Different Fxml - Javafx

I am new to JavaFX. I have a window loaded with vertical split pane. Here left side of the split page I have couple of buttons. On Each button click I need to load separate fxml on the right side of the split pane. So here I paste screen shot to be clear.
Here as of now I am opening in separate stage, separate scene when search button is hit. Now I need to load Searcher in the right side of baselayout window. Here is some code which loads baseLayout.
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Base Layout");
BaseController.setMlTool(this);
FXMLLoader loader = new FXMLLoader(MLTool.class.getResource("view/Base.fxml"));
baseLayout = (AnchorPane) loader.load();
Scene scene = new Scene(baseLayout);
primaryStage.setScene(scene);
primaryStage.show();
}
Here is some code which loads Searcher on button click.
#FXML
private void initialize(){
System.out.println("Testing");
}
#FXML
private void handleSearchButton(){
System.out.println("Handle Button Called");
Stage search = new Stage();
FXMLLoader loader = new FXMLLoader(MLTool.class.getResource("view/Searcher.fxml"));
search.setTitle("Searcher");
try {
AnchorPane searcherPage = (AnchorPane) loader.load();
Scene scene = new Scene(searcherPage);
search.setScene(scene);
search.show();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Here how do I load Searcher in BaseLayout's Rightside of split pane.
Hope my question is clear. Thanks for your help in anticipation.
You can take pane on right side and then load fxml as child node of that pane. Stage is same for all time and you can load fxml in pane.
ex: MainWindow.fxml is perent window. You can load in stage.
Parent root = FXMLLoader.load(getClass().getResource("MainWindow.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
Now in MainWindow.fxml you have split pane. Put anchorPan on right side where you want to load fxml on button click. Then add fxml as child in anchorpane.
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(fxml));
Pane cmdPane = (Pane) fxmlLoader.load();
try {
anchCmdController.getChildren().clear();
anchCmdController.getChildren().add(cmdPane);
fadeIn.playFromStart();
} catch (Exception e) {
e.printStackTrace();
}
try to listen to the button click, once its clicked use 'FXMLLoader.load' to load based on the root elenment, typecast it say panel and assign tot he content of the right side.
In Your handler method try with this:
first declare anchorpane. In fxml right pane put anchorpane with anchCmdController name.
#FXML
private AnchorPane anchCmdController;
then update your handleSearchButton event.
#FXML
private void handleSearchButton() throws IOException {
System.out.println("Handle Button Called");
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("view/Searcher.fxml"));
Pane cmdPane = (Pane) fxmlLoader.load();
try {
/* NEED TO DECLARE ANCHOR PANE" */
anchCmdController.getChildren().clear();
anchCmdController.getChildren().add(cmdPane);
} catch (Exception e) {
e.printStackTrace();
}
}

JavaFX - how to set default active control

I've got single window JavaFX application, created from one of the JavaFX tutorial.
I'm setting new window content by following function:
private Initializable replaceSceneContent(final String fxml) throws Exception {
// wczytanie fxml
FXMLLoader loader = new FXMLLoader();
InputStream in = Main.class.getResourceAsStream(fxml);
loader.setBuilderFactory(new JavaFXBuilderFactory());
loader.setLocation(Main.class.getResource(fxml));
AnchorPane page;
try {
page = (AnchorPane) loader.load(in);
} finally {
in.close();
}
Scene scene = new Scene(page, w, h);
stage.setScene(scene);
return (Initializable) loader.getController();
}
But i want to choose one of TextFields from this fxml file to be active by default. How to do this? I've tried to call requestFocus method in controller's initialize method but it didn't work. I haven't found any suitable property in TextField class either in AnchorPane class (AnchorPane is root element of fxml controls tree).
Try wrapping your requestFocus() call with PlatForm.runlater
Platform.runLater(new Runnable() {
public void run() {
textField.requestFocus();
}
});

Categories

Resources