Getting Null Pointer Exception while removing a layer in Javafx - layout

I have three layers
Root layout, home, content (rootLayout.fxml, home.fxml and content.fxml)
FXMLLoader loader = new FXMLLoader();
loader.setLocation(GenerateReport.class
.getResource("/skin/rootLayout.fxml"));
rootLayout = (AnchorPane) loader.load();
Scene scene = new Scene(rootLayout);
FXMLLoader loader = new FXMLLoader();
loader.setLocation(GenerateReport.class
.getResource("/skin/home.fxml"));
AnchorPane homeLayout = (AnchorPane) loader.load();
rootLayout.getChildren().addAll(homeLayout);
.
.
.
rootLayout.getChildren().addAll(contentLayout);
like this I am adding content layout. In rootLayout.fxml i have a home button. My requiredment is if a user clicks home button
then i want content layout to be removed and home layout to be visible.
content.fxml
<AnchorPane id="myContent" ... fx:controller="com.ReportController">
rootLayout.fxml
<AnchorPane id="AnchorPane" .. fx:controller="com.ReportController">
<children>
<Button fx:id="home" onAction="#homeBtn" .../>
</children>
</AnchorPane>
In my Controller (In all the fxml file i am pointing to the same controller) class i created
#FXML
private Button home;
#FXML
private AnchorPane myContent;
#FXML
protected void homeBtn(ActionEvent event) throws Exception {
System.out.println("click! homeBtn");
myContent.getChildren().clear();
}
The problem i am facing is i am getting NullPointerException. i.e. myContent.getChildren() is returning null. Could anyone help me in resolving this issue.

You're getting a Null Pointer Exception because Javafx doesn't associate your myContent with the AnchorPane stored in the fxml, and does not attach a reference to that object.
Nodes in fxml files are given their name identifiers by using fx:id. Note, for example, that your #FXML private Button home is marked in the fxml as <Button fx:id="home"...>.
In this case, your #FXML AnchorPane myContent is marked in fxml as <AnchorPane id="myContent"...>, not <AnchorPane fx:id="myContent"...>.
id="" is used only in CSS.

Related

Connected styles in Scene Builder is not found in runtime

I create Pane in Scene Builder for Java 8. My .css files store in /rescouces/css/app.css. I connect stylesheet in Scene Builder and all ok. But after i start my app i get exception with error:
Caused by: javafx.fxml.LoadException: Invalid resource: /../style/app.css not found on the classpath.
How to fix this? I need every time rename path to css in .fxml?
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="912.0" prefWidth="1368.0" styleClass="app" stylesheets="#/style/app.css" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.mypod.tablet.controller.MainController">
<children>
<AnchorPane fx:id="contentPane" layoutX="248.0" layoutY="138.0" stylesheets="#/style/content.css" AnchorPane.bottomAnchor="94.0" AnchorPane.leftAnchor="250.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="115.0">
<styleClass>
<String fx:value="block" />
<String fx:value="content-block" />
</styleClass>
</AnchorPane>
</children>
</AnchorPane>
Load fxml:
FXMLLoader loader = new FXMLLoader();
this.primaryStage.setScene(new Scene(loader.load(Util.getResource("/fxml/main.fxml"))));
The problem
In order to reproduce the issue, and based on the comments for the question, this is required:
Main class
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(MainApp.class.getClassLoader().getResourceAsStream("fxml/scene.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
FXML under src/main/resources/fxml/scene.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" stylesheets="#../styles/styles.css" prefHeight="200" prefWidth="320" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Button fx:id="button" text="Click Me!" />
</children>
</AnchorPane>
CSS under src/main/resources/styles/styles.css
.button {
-fx-font-size: 2em;
}
The project runs, but you get this error printed:
null/../styles/styles.css
com.sun.javafx.css.StyleManager loadStylesheetUnPrivileged
WARNING: Resource "../styles/styles.css" not found.
While manually editing the FXML file and removing the parent dots:
stylesheets="#/styles/styles.css"
seems to solve the issue and runs fine without warning, this prevents Scene Builder from finding the css file, so it shouldn't be done.
The solution
Using getResourceAsStream to retrieve the FXML file is not recommended, just use getResource().
This works:
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(MainApp.class.getClassLoader().getResource("fxml/scene.fxml"));
Using the FXMLLoader empty constructor is not the recommended way, instead use the the static load method.
This works:
Parent root = FXMLLoader.load(MainApp.class.getClassLoader().getResource("fxml/scene.fxml"));
Finally, there is no need for class loader. The classloader is stream based, and it doesn't know about the class you retrieve it from and tries to locate from the package "fxml/scene.fxml". On the other hand, Class.getResource() is URL based and it looks up the resource relative to the class, so you need to set the path to the root of the project "/fxml/scene.fxml".
This is how it should be called:
Parent root = FXMLLoader.load(MainApp.class.getResource("/fxml/scene.fxml"));
Or in case you need the loader (for instance to retrieve the controller), this is also the recommended way:
FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("/fxml/scene.fxml"));
// YourController controller = (YourController) loader.getController();
Parent root = loader.load();
This post is worth reading.

Accessing scene from controller class

I'm new to and trying to learning JavaFX and FXML. Most of my application logic is in the FXMLController class and the base class is pretty much empty except the basic code that was generated by NetBeans IDE such as below
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
I have an element with ID input1 that is of type TextField. How can i access this (or any
other) control by its ID? (keeping in mind that I am in the controller class and not the main class).
I found this question below which is exactly what I'm looking for but that situation is different because they are in the main class where scene is defined. How can i access scene from the controller class and use the code in the question below.
How to find an element with an ID in JavaFX?
This is my first time answering a question on Stack Overflow so please go easy on me.
I am new to JavaFX as well and I too had a problem with this.
This is what if found.
Your TextField in your FXMLDocument.fxml must have a fx:id assigned to it, as in:
<TextField fx:id="input1" layoutX="0.5" layoutX="0.5" />
If you are using the JavaFX SceneBuilder then you can find the fx:id under "Code: TextField" on the right side.
Then in your controller class you can access it but using.
#FXML public TextField input1;
You can use an ArrayList to loop through all of your TextFields.
Here is an example.
#FXML public TextField input1;
#FXML public TextField input2;
#FXML public TextField input3;
#FXML public TextField input4;
#FXML public TextField input5;
#FXML public TextField input6;
#FXML public TextField input7;
#FXML public Button button;
List<TextField> inputs = new ArrayList<TextField>();
public void displayText(ActionEvent event) {
inputs.add(input1);
inputs.add(input2);
inputs.add(input3);
inputs.add(input4);
inputs.add(input5);
inputs.add(input6);
inputs.add(input7);
for (int x = 0; x < 7; x++) {
System.out.println(inputs.get(x).getText());
}
}
There might be a simpler way, but this way works for me.

JavaFX / how to load/populate values at start up?

I started working with JavaFX just today and already need some advise. I load the applicaton.fxml (created with Oracle SceneBuiler) using the FXMLLoader in the start(Stage ...) method of the MainApplication (which has an ApplicationController specified in my application.fxml file).
<AnchorPane id="AnchorPane" disable="false" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" styleClass="theme" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="app.AppController">
...more code here...
<ComboBox id="cmb_locations" fx:id="cmb_locations">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="Item 1" />
<String fx:value="Item 2" />
<String fx:value="Item 3" />
</FXCollections>
</items>
</ComboBox>
Now, I have a ComboBox in the applicaton.fxml, which has three items (the default items). What I need is to populate that ComboBox during the startup with my own values. Does anyone know how to achieve that and where to put the relevant code snippets (app.AppController or something similar)? Thanks in advance.
You have some controller for you fxml file. There you have access to your ComboBox. You could put this code to setup list of elements (probably in initialize() method):
If you don't really want to edit your fxml file you can just clear the list first with cmb_locations.getItems().clear(); before you setup new list.
public class ApplicationController implements Initializable {
#FXML
ComboBox cmb_locations;
...
#Override
public void initialize(URL url, ResourceBundle rb) {
...
List<String> list = new ArrayList<String>();
list.add("Item A");
list.add("Item B");
list.add("Item C");
ObservableList obList = FXCollections.observableList(list);
cmb_locations.getItems().clear();
cmb_locations.setItems(obList);
...
}
}
Start by removing the default values on the FXML "Item 1" "Item 2" ...
just to have
<FXCollections fx:factory="observableArrayList">
</FXCollections>
and on your controller if you want retrieve your combobox you have to inject it by doing
#FXML
ComboBox cmb_locations
public void initialize(URL url, ResourceBundle resource) {
//here populate your combobox
}
In your controller, you implement the Initializable interface.
Then in initialize method, you just add your code to load your combo box.

Adding JavaFX2 Controls dynamically

I'm quite new to java and javafx and have a problem which i could not solve.
I need to dynamically add new custom controlls to a javafx scene. Further i need interaction between the main control and the added controls.
I found already some useful information in the web but could not put it together.
So i build a little example for explanation:
main class:
public class Test_TwoController extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Fxml1.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The main fxml:
<AnchorPane id="fxml1_anchorpane_id" fx:id="fxml1_anchorpane" prefHeight="206.0" prefWidth="406.0" xmlns:fx="http://javafx.com/fxml" fx:controller="test_twocontroller.Fxml1Controller">
<children>
<HBox id="fxml1_hbox_id" fx:id="fxml1_hbox" prefHeight="200.0" prefWidth="400.0">
<children>
<Button id="fxml1_button_id" fx:id="fxml1_button" mnemonicParsing="false" onAction="#button_action" prefHeight="200.0" prefWidth="200.0" text="Button" />
</children>
</HBox>
</children>
</AnchorPane>
and its controller:
public class Fxml1Controller implements Initializable {
#FXML HBox hbox;
#FXML Button button;
#Override
public void initialize(URL url, ResourceBundle rb) { }
public void button_action(ActionEvent event) throws IOException {
// 1. add an instance of Fxml2 to hbox
// 2. change to tab2 in new Fxml2
// or
// notify Fxml2Controller to change to tab2 in Fxml2
}
}
And now the control to dynamically add:
Its fxml:
<AnchorPane id="fxml2_anchorpane_id" fx:id="fxml2_anchorpane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0" prefWidth="200.0" xmlns:fx="http://javafx.com/fxml" fx:controller="test_twocontroller.Fxml2Controller">
<children>
<TabPane id="fxml2_tabpane_id" fx:id="fxml2_tabpane" prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE">
<tabs>
<Tab id="fxml2_tab1_id" fx:id="fxml2_tab1" text="tab1">
<content>
<AnchorPane id="Content" minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
</content>
</Tab>
<Tab id="fxml2_tab2_id" fx:id="fxml2_tab2" onSelectionChanged="#onSelectionChanged" text="tab2">
<content>
<AnchorPane id="Content" minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
</content>
</Tab>
</tabs>
</TabPane>
</children>
</AnchorPane>
and the controler:
public class Fxml2Controller {
#FXML TabPane tabpane;
#FXML Tab tab1;
#FXML Tab tab2;
public Fxml2Controller() throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("Fxml2.fxml"));
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.setScene(scene);
}
public void onSelectionChanged(Event e) throws IOException {
FXMLLoader loader = new FXMLLoader();
// how can i get the current Fxml1 anchorpane instance?
AnchorPane root = (AnchorPane) loader.load(getClass().getResource("Fxml1.fxml").openStream());
Button b = (Button)root.lookup("#fxml1_button_id");
b.setText("New Button Text"); // dont change the buttons text!!!
}
}
The usage is: A fxml2 should be added to the hbox of fxml1. Then after a button click in fxml1 the tabs of fxml2 should change.
You may have a look at that image http://s13.postimage.org/uyrmgylo7/two_controlls.png
So my questions are:
how can i add one or more of the fxml2 controller into the hbox of fxml1?
how can i access one control from another or communicate between controlls? See onSelectionChanged() method in Fxml2Controller for detail.
Thank you in advance,
solarisx
You seem to have mixed quite a few concepts together which are distinct. First of all, a Stage can be understood as a window on the screen. It has a Scene object which holds the actual SceneGraph. In your example, you are creating a new Stage and a new Scene that get filled which the content of your second fxml-file. This means that, if working, a second window will pop up containing your stuff. I don't think that this is what you want to achieve.
Moreover, when the FXMLLoader reads a file, it looks for the class that is specified as its controller and constructs an instance of it via reflection. This means that when you call the load method in the constructor of the controller of the fxml-file you are loading with it, you are causing an infinite loop.
The last thing to understand is that the object that load() returns is an arbitrary node which can be put into the SceneGraph of your application just like any other node.
So to make your concept work, you should make the following:
Move the loading-code which is currently in the constructor of your second controller to the button_Action method of your first controller.
Throw away the new-stage-new-scene code in the button_action and take the Node returned by the FXMLLoader and add it to the children of the HBox.
For your second question, you can get the controller instance if you actually create an instance of an FXMLLoader instead of calling the static method, and use the load() method in it. After calling load() you can retrieve the controller and root object of the fxml-file via getController() and getRoot(). You can then use them just like any arbitrary object in your logic.

javafx - updating values in fxml object which was dynamically loaded on button click

I am creating a simple Weather Application
Here goes the details of my question :-
I have a main Controller (GeoWeatherMain.java). When Appl is run, this class(GeoWeatherMain.class) gets loaded which loads an fxml file.
Below is code for it :-
Parent root = FXMLLoader.load(getClass().getResource("GeoWeatherMainUI.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
Now, GeoWeatherMainUI.fxml has BorderPane object implementation. It consists of a button(on the left pane) which onclick loads another fxml file inside center pane. The skeleton of GeoWeatherMainUI.fxml looks like below:-
<BorderPane fx:id="MainBody" prefHeight="736.0" prefWidth="1140.0" xmlns:fx="http://javafx.com/fxml" fx:controller="geoweather.GeoWeatherUIActionHandlers">
.
.
<Button layoutX="91.0" layoutY="67.0" mnemonicParsing="false" onAction="#getCurrAndForecastWeatherCond" text="Get Weather Details" />
.
.
<BorderPane>
Now GeoWeatherUIActionHandlers.java is another controller which takes care of different button action events.Below is its complete code
public class GeoWeatherUIActionHandlers implements Initializable{
#FXML
BorderPane MainBody;
#FXML
Label LocName;/*LocName is fx id for a label*/
#FXML
private void getCurrAndForecastWeatherCond(ActionEvent event){
try{
Pane centerPane = FXMLLoader.load(getClass().getResource("WeatherDetails.fxml"));
MainBody.setCenter(centerPane);
/*LocName.setText("xyz");*/
}catch (IOException ex){
TextArea excep = new TextArea(ex.toString());
MainBody.setCenter(excep);
}catch(Exception e){
TextArea excep = new TextArea("Inside Exception : \n" + e.toString());
MainBody.setCenter(excep);
}
}
#Override
public void initialize(URL url, ResourceBundle rb){}
}
Now, if I want to update the loaded WeatherDetails.fxml file with new values, how to do that? I tried as in the above commented code.(LocName.setText("xyz")). But, it didn't work (giving NullPointerException).
I went through complete documentation of javafx # docs.oracle.com. No luck. Here also I didn't get the answer. Please guide.
If the LocName is located inside WeatherDetails.fxml then it is excepted behavior that the LocName will be null. Because #FXML Label LocName; is defined in GeoWeatherUIActionHandlers.java which is a controller of GeoWeatherMainUI.fxml. Move LocName from WeatherDetails to GeoWeatherMainUI FXML file and see where you are still getting the error or not.
If your aim is to set the text of the label that is inside WeatherDetails.fxml, then do this work
in the controller of WeatherDetails similar to current GeoWeatherUIActionHandlers, or
in GeoWeatherUIActionHandlers, after loading the WeatherDetails.fxml
get the label by id (not fx:id) from centerPane with ((Label)centerPane.lookup("#myLabel")).setText("xyz"), or
get the controller of WeatherDetails and call getLocName().setText("xyz"). Assuming getter method exists in a controller class.

Resources