(DUPLICATE & SOLVED - see answer below)
I'm doing my first steps in JavaFX and it seems quite hard to use the "SceneBuilder". I'm used to Android and the QtCreator. It looks to me that there is accessing the UI components much easier.
Something like findViewById(R.id.btnPushMe); <- Android Code
Actually I got an solution but it is quite uncomfortable to use. This looks as this:
FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("../fmxl/main.fxml"));
AnchorPane pane = loader.load();
System.out.println("panechilds:" + pane.getChildren().size());
BorderPane border = (BorderPane) pane.getChildren().get(0);
System.out.println("borderchilds:" + border.getChildren().size());
the xml..
<AnchorPane fx:id="mAnchor" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.progui.MainController">
<children>
<BorderPane layoutX="-1.0" prefHeight="600.0" prefWidth="800.0">
<top>
...
Thanks in advance
Martin
Edit:
This is a duplicate question (but I will not delete it, because I took some time to find the answer - maybe because JavaFX wasn't asked as much as Android questions were..)
AnchorPane anchor = (AnchorPane) scene.lookup("#mAnchor");
found here: How to find an element with an ID in JavaFX?
You should use a controller class and access the UI elements there.
Basically you do:
<AnchorPane fx:id="mAnchor" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.progui.MainController">
<children>
<BorderPane fx:id="border" layoutX="-1.0" prefHeight="600.0" prefWidth="800.0">
<top>
...
And then you can access the fx:id-attributed elements in the controller with
package app.progui ;
// ...
public class MainController {
#FXML
private BorderPane border ;
public void initialize() {
border.setStyle("-fx-background-color: antiquewhite;");
// ...
}
// ...
}
The field names in the controller class must match the fx:id values in the FXML file.
It is possible to access the fx:id-attributed elements in the class that invoked the FXMLLoader, but if you need to do this it is usually a sign that your overall design is wrong. You can do:
FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("../fmxl/main.fxml"));
AnchorPane pane = loader.load();
Map<String, Object> fxmlNamespace = loader.getNamespace();
BorderPane border = (BorderPane) fxmlNamespace.get("border");
assuming the fx:id defined in the FXML snipped above.
When you design for FXML, you typically design three things: the application logic, the GUI controller logic, and the FXML.
References to the UI controls you wish to access are injected by the FXML loader into your controller class during its loading and initialization so that you do not need to use a FindById() method.
A controller class looks similar to this:
class DCServRecEditor extends DialogController {
#FXML // ResourceBundle that was given to the FXMLLoader
private ResourceBundle resources;
#FXML // URL location of the FXML file that was given to the FXMLLoader
private URL location;
#FXML // fx:id="ancMatchSelector"
private AnchorPane ancMatchSelector; // Value injected by FXMLLoader
#FXML // fx:id="ancServEditor"
private AnchorPane ancServEditor; // Value injected by FXMLLoader
#FXML // fx:id="ancServRecEditor"
private AnchorPane ancServRecEditor; // Value injected by FXMLLoader
:
:
The FXML loading facility automatically injects references into instance fields that are annotated with the #FXML tag. To manipulate a UI control, just access its methods using the appropriate reference.
Separating the UI control logic from your application logic is highly desirable, and effects a "separation of concerns". When you get used to designing FXML UI's this way, you'll enjoy it.
Related
Now I'm working with javafx and create small view with SplitPane.
In this SplitPane i have two TiteledPanes(named A and B). At first step of program working, only one TitledPane contains data (A). But after little user actions some information are put on B.
If i run my programm 10 times, at 7-8 times everything will be ok!
But in 2 times I have a problem with second titeledPane. It is consists with no data. This TitledPane loaded without any exceptions and all bindings are good, But it consists with no data. It is very strange, because in 80% of time everything is ok.
May be this is javafx bugs?
Any one have the same problem?
<fx:root type="MyView" dividerPositions="0.5" orientation="VERTICAL" style="-fx-background-color: darkgray">
<fx:define>
<TitledPane fx:id="A">
<AnhorPane>
<Label text="ALabel"/>
</AnhorPane>
</TitledPane>
<TitledPane fx:id="B"/>
<AnhorPane>
<Label text="BLabel"/>
</AnhorPane>
</TitledPane>
</fx:define>
</fx:root>
public class MyView extends SplitPane {
public MyView () {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("MyView .fxml"));
loader.setController(this);
loader.setRoot(this);
loader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
}
The problem was in loading fxml file proccess. There are a lot of data should be loaded after the load fxml file(in my model class relating to that fxml) and in future adding to existing fxml. Like binding properties and etc.
When I relocate above 80% of bindings in fxml file using ${} - the problem was solded.
I am trying to load the FXML file and show it as an application window, but i get an exception. The FXML file was created by the FXML Scene Builder.
Here are the codes for the class
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(FXMLLoader.load(getClass().getResource("sample.fxml")));
primaryStage.show();
}
}
and FXML file
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.layout.AnchorPane?>
<TitledPane animated="false" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="400.0" prefWidth="600.0" text="Pass4D" xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/8">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children>
<Button layoutX="211.0" layoutY="134.0" mnemonicParsing="false" prefHeight="33.0" prefWidth="177.0"
text="Log in"/>
<Button layoutX="212.0" layoutY="170.0" mnemonicParsing="false" prefHeight="33.0" prefWidth="175.0"
text="Exit"/>
</children>
</AnchorPane>
</content>
</TitledPane>
And here is the exception i get
Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:875)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$147(LauncherImpl.java:157)
at com.sun.javafx.application.LauncherImpl$$Lambda$1/2074407503.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException: Location is required.
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3201)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3169)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3142)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3118)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3098)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3091)
at Pass4D.start(Pass4D.java:19)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$153(LauncherImpl.java:821)
at com.sun.javafx.application.LauncherImpl$$Lambda$51/317090070.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$166(PlatformImpl.java:323)
at com.sun.javafx.application.PlatformImpl$$Lambda$47/1833150059.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$null$164(PlatformImpl.java:292)
at com.sun.javafx.application.PlatformImpl$$Lambda$49/2115863517.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$165(PlatformImpl.java:291)
at com.sun.javafx.application.PlatformImpl$$Lambda$48/1436737924.run(Unknown Source)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
What am i doing wrong?
p.s. here is the project structure
The short answer is getClass().getResource("sample.fxml") returns null silently if the resource cannot be found on the runtime classpath, not the current directory etc.
So this depends on your IDE project setup, if you're using eclipse try adding the folder that sample.fxml resides in the run configuration.
Some ideas...
try getClass().getResource("/sample.fxml") instead...
try moving sample.fxml into the resources folder. I don't know much about your IDE, but I suspect that folder is only used for .java files... this is certainly true for gradle projects in eclipse - resources have to be in the src/main/resources tree as only that is added to the runtime classpath...
I already posted this today, so here's again, hope it helps you.
Here's a solution that works in the development environment, in Scene Builder and in a packaged JAR.
The folder structure:
Main.java:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader(Main.class.getResource("view/RootLayout.fxml"));
AnchorPane rootLayout = (AnchorPane) loader.load();
Scene scene = new Scene(rootLayout, 400, 400);
scene.getStylesheets().add(getClass().getResource("css/application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
RootLayout.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.view.RootLayoutController">
<children>
<Pane layoutX="0.0" layoutY="0.0" prefHeight="200.0" prefWidth="200.0">
<children>
<Button fx:id="sunButton" layoutX="74.0" layoutY="88.0" mnemonicParsing="false" onAction="#handleSunButtonClick" styleClass="sun-button" stylesheets="#../css/toolbar.css" text="Button" />
</children>
</Pane>
</children>
</AnchorPane>
RootLayoutController.java:
package application.view;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
public class RootLayoutController {
#FXML
Button sunButton;
#FXML
public void handleSunButtonClick() {
System.out.println( "Button clicked");
}
}
toolbar.css:
.sun-button {
-fx-graphic: url('./icons/sun.png');
}
application.css:
.root {
-fx-background-color:lightgray;
}
sun.png:
This works in both the development environment and when you package the JAR (choose "Extract required libraries into generated JAR" in Eclipse).
Screenshot (just a button with an icon loaded via css)
Try this example from oracle:
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("fxml_example.fxml"));
Scene scene = new Scene(root, 300, 275);
stage.setTitle("FXML Welcome");
stage.setScene(scene);
stage.show();
}
Main.class.getResource worked for me. I was trying to get resource from subclass instead of Main class. I used there getClass().getResource() which was pointing to the current class. I didn't have my FXML files in resources directory.
Append MainApp.class to getClass().getResource("/fxml/Scene.fxml") . worked for me!
example:
Parent root = FXMLLoader.load(MainApp.class.getClass().getResource("/fxml/Scene.fxml"));
Delete the "build" folder in your javafx project and rebuild your project.
If you are using Maven you may need to move the fxml file to your resources directory.
refactor src/main/java/sample/sample.fxml to src/main/resources/sample/sample.fxml
Here's what to do when using an IntelliJ IDE.
When you are using getClass().getResource(), the resource must be located in the resources folder under a directory in that resource folder that corresponds to the package name of the class you are using:
src/main/java/com/foo/MyClass.java
src/main/java/resources/com/foo/MyResource.fxml
MyClass.class.getResource(MyResource.fxml) will worked
same rule applies for using FXMLLoader.
In the IntelliJ world, if you put that resource into the same folder that the class is located in, that resource won't be found. The irony of that is when look at jar produced, you'll find that the resource is actually placed into the same jar folder as the source is located in. That's because, when debugging only the compiled java classes are moved into the target folder, together with the resources from the resources directory. So what's really happening is just an artifact of how the debug build process works. I know that if you were using Android Studio for instance it works slightly different. You could place the resource in the same folder that the java source file is located, and it would find that resource. I don't know what IDE Roland above was using, but he was using an IDE that allowed him to place the resource the same directory as the java source was located in.
for me, after a lot of trial and error, the only way that worked was to add getClassLoader() like so:
MyClass.class.getClassLoader().getResource("views/myview.fxml")
For a layout of:
src
+main
+resources
+views
+myview.fxml
There is an possibility to get Controller instance from Node ?? for example AnchorPane on Tab ?
I have some AnchorPanes where I load different Controllers and I would like to verify which controller is already loaded
Nodes do not contain any information about a controller used with the fxml file used to create it by default, since fxml is just one way of creating a scene. However you could attach information like this to the userData/properties in the fxml:
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" fx:id="AnchorPane" prefHeight="400.0" prefWidth="600.0" onMouseClicked="#click" fx:controller="fxml.FXMLController">
<!-- store controller as userData property -->
<userData>
<fx:reference source="controller" />
</userData>
<properties>
<!-- store controller at key "foo" in properties map -->
<foo><fx:reference source="controller" /></foo>
</properties>
</AnchorPane>
If you do this, you can lookup the controller at closest ancestor of a node where you added this kind of information using
public static Object getController(Node node) {
Object controller = null;
do {
controller = node.getProperties().get("foo");
node = node.getParent();
} while (controller == null && node != null);
return controller;
}
to retrieve the info from the properties map or using
public static Object getController(Node node) {
Object controller = null;
do {
controller = node.getUserData();
node = node.getParent();
} while (controller == null && node != null);
return controller;
}
to retrieve the info from the userData property.
You should probably use just one way of adding the info though, not both. Also it would be better to replace foo as key...
It's an old question, but if you have a main window, where you include other fxml files like this:
<AnchorPane prefHeight="900.0" prefWidth="1600.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="MainController">
<!-- <HBox></HBox>, some elements here, your normal usual FXML-->
<fx:include fx:id="someAnchorPane" source="SomeAnchorPane.fxml"/>
</AnchorPane>
and your SomeAnchorPane.fxml has fx:controller property set to SomeOtherController, then you can add a controller field in your MainController like this:
#FXML private SomeOtherController someAnchorPaneController;
And it will inject appropriate controller in this field automatically.
The key thing here is that your field has to be named "fx:id+Controller" for it to work.
In my program i have a tabPane with each tab having in own FXML file and controller .I have loaded the FXML file for tab 2 (Schedular).I`m trying to call a function in my child controller (Scheduler) from my parent controller(FXML Document) . When i load the controller using FXML Loader it returns a null. How can i solve this .
Here is my main code:
Main FXMLDocument :
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" fx:id="AnchorPane" prefHeight="600" prefWidth="800" xmlns="http://javafx.com/javafx/8.0.65" fx:controller="showprojavafxml.FXMLDocumentController">
<children>
The FXMLLoader only instantiates the controller when it loads the fxml file (this has to be the case, since the controller class is specified in the fxml file...). Since you never call load() on the loader, the controller is never created.
It looks like you are referencing the FXML file twice through two different mechanisms: once in FXMLDocumentController.initialize(), where you create a FXMLLoader whose location is set to the fxml file, and once in the main FXML document itself, via a <fx:include>. The <fx:include> is causing the UI defined in Scheduler.fxml to be displayed; the FXMLLoader you create in the initialize() method is not (because you never call load() and display the result).
To reference a controller for an included fxml, use the "Nested controller" technique.
First, add an fx:id to your fx:include:
<Tab fx:id="tab2" text="Scheduler" >
<fx:include fx:id="scheduler" source="Scheduler.fxml" />
</Tab>
Now you can inject the controller into a field whose name is the fx:id with the text "Controller" appended:
public class FXMLDocumentController {
#FXML
private SchedulerController schedulerController ;
#Override
public void initialize(URL url, ResourceBundle rb) {
schedulerController.refreshList();
}
}
<AnchorPane>
<TreeView fx:id="locationTreeView" focusTraversable="true" prefHeight="449.0" prefWidth="725.0" style="#tree
{
-fx-border-style:solid;
-fx-border-width:1px;
-fx-border-color:#ffffff;
}"/>
In the above fxml code I want to add one more <TreeView> but through the controller. How can I do this?
You will have to:
Give a fx:id to the AnchorPane:
<AnchorPane fx:id="theAnchorPane">
Add the corresponding field in the controller:
#FXML private AnchorPane theAnchorPane;
From the code that performs the addition you have to:
Create the new TreeView however you like:
TreeView newTreeView = ...;
Add it to the childen of the AnchorPane, possibly with some constraints:
theAnchorPane.getChildren().add(newTreeView);
AnchorPane.setTopAnchor(newTreeView, ...); // etc