0
votes

I am trying to implement a set of stacked charts in FXML similar to this example. I'm fairly inexperienced and couldn't find whole examples of setting up CSS in FXML Controller. Trying to adapt code myself has resulted in either the java.lang.NullPointerException exception, `loadStylesheetUnPrivileged' message, or both, and the code either doesn't run, or runs without any changes to the CSS. SceneBuilder is able to show a transparent preview, but it doesn't work when running the program.

FXML:

<AnchorPane fx:id="graphpane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Graph1Controller">
<children>
  <StackPane fx:id="stackpane" prefHeight="150.0" prefWidth="200.0" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0">
     <children>
        <LineChart fx:id="M1chart" legendVisible="false" prefHeight="388.0" prefWidth="573.0" style=".chart-plot-background{-fx-background-color: transparent;} .default-color0.chart-series-line{-fx-stroke: #006564;}" stylesheets="@..css/M1chart.css" title="Recent Lap Times">
          <xAxis>
            <NumberAxis fx:id="M1xAxis" forceZeroInRange="false" minorTickVisible="false" side="BOTTOM" tickLabelsVisible="false" tickMarkVisible="false" />
          </xAxis>
          <yAxis>
            <NumberAxis fx:id="M1yAxis" autoRanging="false" label="Laptime (s)" side="LEFT" upperBound="240.0" />
          </yAxis>
        </LineChart>
        <LineChart fx:id="M2chart" legendVisible="false" prefHeight="388.0" prefWidth="573.0" style=".chart-plot-background{-fx-background-color: transparent;} .default-color0.chart-series-line{-fx-stroke: #FFCE44;}" stylesheets="@..css/M2chart.css" title="Recent Lap Times">
           <xAxis>
              <NumberAxis fx:id="M2xAxis" forceZeroInRange="false" minorTickVisible="false" side="BOTTOM" tickLabelsVisible="false" tickMarkVisible="false" />
           </xAxis>
           <yAxis>
              <NumberAxis fx:id="M2yAxis" autoRanging="false" label="Laptime (s)" side="LEFT" upperBound="240.0" />
           </yAxis>
        </LineChart>
        <LineChart fx:id="L1chart" legendVisible="false" prefHeight="388.0" prefWidth="573.0" style=".chart-plot-background{-fx-background-color: transparent;} .default-color0.chart-series-line{-fx-stroke: #832C40;}" stylesheets="@..css/L1chart.css" title="Recent Lap Times">
           <xAxis>
              <NumberAxis fx:id="L1xAxis" forceZeroInRange="false" minorTickVisible="false" side="BOTTOM" tickLabelsVisible="false" tickMarkVisible="false" />
           </xAxis>
           <yAxis>
              <NumberAxis fx:id="L1yAxis" autoRanging="false" label="Laptime (s)" side="LEFT" upperBound="240.0" />
           </yAxis>
        </LineChart>
     </children>
  </StackPane>
</children>   
</AnchorPane>

There are 3 CSS sheets I want to load, each of which is in the same folder (css), the folder placed in the same directory as the Java source files, and each is structured similar to this:

CSS:

.chart-plot-background{
-fx-background-color: transparent;
}

.default-color0.chart-series-line{
-fx-stroke: #832C40;
}

In the main class I've tried to follow Roland's answer in this thread but it doesn't seem to work.

Main:

public void start(Stage primaryStage) throws IOException {

//First FXML Controller code omitted

//Second FXML Controller code starts here
Stage stage2 = new Stage();
FXMLLoader loader2 = new FXMLLoader();
String fxmlDocPath2 = "(filepath)\\Graph1.fxml"; //actual filepath replaced with placeholder for this post
FileInputStream fxmlStream2 = new FileInputStream(fxmlDocPath2);
AnchorPane root2 = (AnchorPane) loader2.load(fxmlStream2);
Scene scene2 = new Scene(root2);
scene2.getStylesheets().add(getClass().getResource("css/M1chart.css").toExternalForm());
scene2.getStylesheets().add(getClass().getResource("css/M2chart.css").toExternalForm());
scene2.getStylesheets().add(getClass().getResource("css/L1chart.css").toExternalForm());
stage2.setScene(scene2);
stage2.setTitle("TEST");
stage2.setX(primaryStage.getX() + 550);
stage2.setY(primaryStage.getY());
stage2.show();
//Second FXML Controller code ends
}

Running this code gives me the errors below:

null/..css/M1chart.css
null/..css/M2chart.css
null/..css/L1chart.css
Oct 10, 2018 10:45:22 PM com.sun.javafx.css.StyleManager loadStylesheetUnPrivileged
WARNING: Resource "..css/M1chart.css" not found.
Oct 10, 2018 10:45:22 PM com.sun.javafx.css.StyleManager loadStylesheetUnPrivileged
WARNING: Resource "..css/M2chart.css" not found.
Oct 10, 2018 10:45:22 PM com.sun.javafx.css.StyleManager loadStylesheetUnPrivileged
WARNING: Resource "..css/L1chart.css" not found.
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(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.base/java.lang.reflect.Method.invoke(Unknown Source)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(Unknown Source)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(Unknown Source)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.base/java.lang.reflect.Method.invoke(Unknown Source)
    at java.base/sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(Unknown Source)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(Unknown Source)
    at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NullPointerException
    at application.Core.start(Core.java:71)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(Unknown Source)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$11(Unknown Source)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$9(Unknown Source)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(Unknown Source)
    at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(Unknown Source)
    ... 1 more
Exception running application application.Core

I'm not sure what I've missed to cause the null to appear in from on the css path and the code to fail, could someone please help?

1
Where exactly is css folder located? You're using relative path, is css folder in the same package as your main class (package application, judging from the stack trace)? If css folder is in the classpath root instead, you should use absolute path: getClass().getResource("/css/M1chart.css"). - Guest 21
The CSS path is in the same folder as the main class of the application, yes. If I change to use the absolute path getClass().getResource("/css/M1chart.css"), do I need to change anything in the FXML file? Thanks for your reply! - Kyle

1 Answers

0
votes

There are 3 CSS sheets I want to load, each of which is in the same folder (css), the folder placed in the same directory as the Java source files

You should move your css/fxml files into a separate resource folder - some build tools won't even copy your resources to output directory if they are in the same folder as source files.

Let's say that your project structure is this:

YourApplicationRoot
 |--res   //resource root
 |  |-fxml
 |  | |-Main.fxml
 |  |-css
 |    |-styles.css
 |--src   //source root
    |-application
      |-Core.java //main class is here

Firstly, don't use inline styles in FXML (style="some rules here") - it's hard to maintain.

Next, you don't really need 3 separate CSS files, you can put all you css styles into one file (styles.css) and use custom style classes to separate them:

/* These styles will only be applied to a chart with class `m1chart` */ 
.m1chart .chart-plot-background{
  -fx-background-color: transparent;
}
.m1chart .default-color0.chart-series-line{
  -fx-stroke: red;
}

/* These styles will only be applied to a chart with class `m2chart` */
.m2chart .chart-plot-background{
  -fx-background-color: transparent;
}
.m2chart .default-color0.chart-series-line{
  -fx-stroke: blue;
}

/* These styles will only be applied to a chart with class `l1chart` */
.l1chart .chart-plot-background{
  -fx-background-color: transparent;
}
.l1chart .default-color0.chart-series-line{
  -fx-stroke: green;
}

Now, you have two ways to apply css to your application - from fxml and from code:

To apply styles.css from fxml, just add stylesheets attribute to root node and add styleClass attribute to each LineChart you want to style:

/* Main.fxml, irrelevant parts are omitted */
<AnchorPane stylesheets="@../css/styles.css">
   ...
   <LineChart styleClass="m1chart">
      ...
   </LineChart>
   <LineChart styleClass="m2chart">
      ...
   </LineChart>
   <LineChart styleClass="l1chart">
      ...
   </LineChart> 
   ...   
</AnchorPane>

Don't forget to remove style and stylesheets attributes from LineChart nodes.

To apply styles.css directly from code (note that your css styles won't be visible in SceneBuilder if you're using this method):

//in Core.java start() method
yourScene.getStylesheets().add(getClass().getResource("/css/styles.css").toExternalForm());

//in your fxml controller
M1chart.getStyleClass().add("m1chart");
M2chart.getStyleClass().add("m2chart");
L1chart.getStyleClass().add("l1chart");