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 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 :
.....
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
解决方案
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 Circles 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);
}
}
xmlns:fx="http://javafx.com/fxml/1">
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:
.....