mapping nested object from fxml to object

不羁岁月 提交于 2020-01-25 07:56:22

问题


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 doesnt 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

回答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>
    .....


来源:https://stackoverflow.com/questions/59768207/mapping-nested-object-from-fxml-to-object

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!