Issue
I created the following object which should be responsible for displaying a steeringwheel in my win app.
@DefaultProperty("children")
public class SteeringWheel extends Region {
@FXML
private Circle backgroundCircle;
@FXML
private Circle innerCircle;
@FXML
private Label mylabel;
public ObservableList<Node> getChildren() {
return super.getChildren();
}
public void setCirclesLocations() {
double centerPointX = getWidth() / 2;
double centerPointY = getHeight() / 2;
setCircleLocation(backgroundCircle, centerPointX, centerPointY);
setCircleLocation(innerCircle, centerPointX, centerPointY);
}
private void setCircleLocation(Circle c, double x, double y) {
c.setCenterX(x);
c.setCenterY(y);
}
and my fxml file contains the following decleration :
<BorderPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/9.0.1" fx:controller="view.WindowController">
<center>
<VBox prefHeight="200" prefWidth="200" BorderPane.alignment="CENTER">
<children>
<SteeringWheel fx:id="steeringWheel">
<children>
<Label fx:id="mylabel" prefHeight="30.0" prefWidth="102.0" text="aileron" />
<Circle fx:id="innerCircle" fill="black" radius="40" />
<Circle fx:id="backgroundCircle" fill="darkgray" radius="100" />
</children>
</SteeringWheel>
</children>
</VBox>
</center>
.....
I'm trying to map the xml initialization to this object but it doesn't work. In my main I'm trying to run the setCircleLocations but I'm getting nullpointer exception.
FXMLLoader fxl=new FXMLLoader();
try {
BorderPane root = fxl.load(getClass().getResource("Window.fxml").openStream());
WindowController wc=fxl.getController(); // View
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
wc.steeringWheel.setCirclesLocations();
} catch (IOException e) {
e.printStackTrace();
}
my window controller :
public class WindowController {
@FXML
SteeringWheel steeringWheel;
}
in addition, in the xml file I'm getting the following error for the children of the SteeringWheel : Unresolved fx:id reference
stack trace :
Exception in Application start method
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:473)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:372)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: Exception in Application start method
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:973)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:198)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.NullPointerException
at view.WheelingSteer.setCircleLocation(WheelingSteer.java:36)
at view.WheelingSteer.setCirclesLocations(WheelingSteer.java:31)
at view.Main.start(Main.java:33)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:919)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$11(PlatformImpl.java:449)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$9(PlatformImpl.java:418)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:417)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:175)
... 1 more
Exception running application view.Main
Solution
Only the controller has the appropriate fields injected. Your SteeringWheel
class is not a controller, thus those @FXML
annotated fields are not injected. The objects you intend to be injected into those fields are put in the children
list, though, so you could access them there. However, it appears you want SteeringWheel
to be its own component, where the two Circle
s and Label
are always present, regardless of how the SteeringWheel
is used, so it doesn't make sense to try and define the children in a separate location.
Since you are extending Region
you may want to consider using fx:root
:
import java.io.IOException;
import java.io.UncheckedIOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
public class SteeringWheel extends Pane {
@FXML private Label myLabel;
@FXML private Circle innerCircle;
@FXML private Circle backgroundCircle;
public SteeringWheel() {
FXMLLoader loader = new FXMLLoader(/* location */);
loader.setRoot(this);
loader.setController(this);
try {
loader.load();
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
@FXML
private void initialize() {
// perform any initialization, if needed
}
@Override
public void layoutChildren() {
// Note: I didn't implement checking if the nodes are managed
// before laying them out. You may wish to add that
// behavior.
// The following will always keep the Circles in the center
// of the Pane. However, it does this by setting the layout[X|Y]
// properties rather than the center[X|Y] properties (as you're
// doing).
double x = snappedLeftInset();
double y = snappedTopInset();
double w = getWidth() - snappedRightInset() - x;
double h = getHeight() - snappedBottomInset() - y;
positionInArea(innerCircle, x, y, w, h, -1, HPos.CENTER, VPos.CENTER);
positionInArea(backgroundCircle, x, y, w, h, -1, HPos.CENTER, VPos.CENTER);
// Layout the Label in the top-left corner
layoutInArea(myLabel, x, y, w, h, -1, HPos.LEFT, VPos.TOP);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.shape.Circle?>
<fx:root type="javafx.scene.layout.Pane" xmlns="http://javafx.com/javafx/9.0.1"
xmlns:fx="http://javafx.com/fxml/1">
<Label fx:id="myLabel" text="Hello, World!"/>
<Circle fx:id="backgroundCircle" radius="30" fill="BLACK"/>
<Circle fx:id="innerCircle" radius="25" fill="FIREBRICK"/>
</fx:root>
Note: I extended Pane
instead of Region
. The former already extends the latter and already makes the changes you made (i.e. makes the #getChildren()
method public and adds a @DefaultProperty("children")
annotation).
Note: I overrode #layoutChildren()
so that your circles remain in the center no matter what dimensions the parent ends up with. However, it may be easier to simply wrap them in some other layout, such as a StackPane
.
Then you would just use SteeringWheel
in your other FXML file:
<BorderPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/9.0.1" fx:controller="view.WindowController">
<center>
<VBox prefHeight="200" prefWidth="200" BorderPane.alignment="CENTER">
<children>
<SteeringWheel fx:id="steeringWheel"/>
</children>
</VBox>
</center>
.....
Answered By - Slaw