0

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
halfer
  • 19,824
  • 17
  • 99
  • 186
JeyJ
  • 3,582
  • 4
  • 35
  • 83
  • _I'm getting nullpointer exception_ Please [edit] your question and post the entire stack trace for the `NullPointerException` you are getting. – Abra Jan 16 '20 at 11:20
  • @Albra done - added the stack trace – JeyJ Jan 16 '20 at 11:28
  • Where is your FXML file that corresponds to `SteeringWheel`? I assume you're trying to use the `fx:root` construct? – Slaw Jan 16 '20 at 11:29
  • Do I need a different FXML file for SteeringWheel ? Cant I use the same FXML file ? – JeyJ Jan 16 '20 at 11:38
  • From what you posted, it looks like one (or both) of `backgroundCircle` or `innerCircle` is not getting initialized. The `FXMLLoader` should handle that based on your code (which should contain `@FXML` annotations). – Abra Jan 16 '20 at 11:38
  • @Abra - yeah, it looks like it doesnt created them.. – JeyJ Jan 16 '20 at 11:39
  • @Slaw do I need an FXML file per object ? Can u explain a little bit more – JeyJ Jan 16 '20 at 11:44

1 Answers1

1

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);
  }
}
<?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>
    .....
Slaw
  • 37,820
  • 8
  • 53
  • 80
  • I tried a different solution and it worked. I'll be happy if u share your opinon. Instead of extensing Pane, I tried to extend VBOX. this solved the null pointer exception. However, the circles werent draw in the center – JeyJ Jan 16 '20 at 12:15
  • You can extend whichever layout fits your needs, I simply chose `Pane` because it matched the code in your question. As for the circles not being drawn in the center, that's because I didn't set the `radius` properties or set the `fill` properties to different colors. In other words, the circles were in the center they just didn't have any size and thus weren't visible. I edited my answer. Note I also switched the `innerCircle` and `backgroundCircle` in the FXML file so that the latter would be drawn behind the former. – Slaw Jan 16 '20 at 12:23
  • I added a radius, but I wanted them to be in the same location(one circle inside the other) – JeyJ Jan 16 '20 at 12:27
  • I'm trying to solve it without having another fxml file and another loader in the SteeringWheel. – JeyJ Jan 16 '20 at 12:29
  • Then you're probably better off doing everything in code within the `SteeringWheel` class or just not having a `SteeringWheel` class at all and simply create the necessary layout in the original FXML file. – Slaw Jan 16 '20 at 12:31
  • You could look into using [`fx:include`](https://openjfx.io/javadoc/13/javafx.fxml/javafx/fxml/doc-files/introduction_to_fxml.html#include_elements) and turn `SteeringWheel` into a controller (rather than a view). This will still give you a second FXML file but you won't have to explicitly load it. – Slaw Jan 16 '20 at 12:34
  • I see. I think that I will move all the parts to the controller. thanks ! – JeyJ Jan 16 '20 at 12:37
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/206067/discussion-between-jeyj-and-slaw). – JeyJ Jan 16 '20 at 12:37
  • Hey, I tried to use the solution that u suggested, if the steerwheel is only one component of a big main panel, should I see initialize the fxml loader in the steerwheel constructor ? – JeyJ Jan 17 '20 at 10:28
  • The [comment by fabian](https://stackoverflow.com/questions/59785959/javafx-doesnt-display-a-nested-component#comment105723937_59785959) seems to point out the problem (as well as another). If that doesn't help, I suggest editing _that_ question to add a [mre]. – Slaw Jan 17 '20 at 20:37
  • I added the center tag but still, i'm not sure why the layout still is empty – JeyJ Jan 18 '20 at 10:38