0

I am trying to write a custom graphing components that I can reuse to draw graphs on multiple tabs within an application.

A single instance of the graph is composed of an FXML file consisting of an AnchorPane with 5 Canvas all on top of each other for each series I would like to display. Each Canvas is assigned an fx:id. I then have a graphFXMLController.java file associated with this, each canvas is given a GraphicsContext, and when running a function I can draw upon each canvas.

On my main application controller file, I declare the component with:

@FXML
public GraphFXMLController spectrumgraph; 

In my main application FXML file I import my graphFXMLController file with:

<?import measuremefx.GraphFXMLController?>

and then I declare my component with:

<GraphFXMLController id="AnchorPane" fx:id="spectrumgraph" layoutX="-23.0" layoutY="314.0" prefHeight="357.0" prefWidth="601.0" /> 

My GraphFXMLController file is:

public class GraphFXMLController extends AnchorPane implements Initializable  {

@FXML 
public Canvas backgroundCanvas;

@FXML
public Canvas series1Canvas;

@FXML
public Canvas series2Canvas;

@FXML
public Canvas series3Canvas;

@FXML
public Canvas series4Canvas;

@FXML
public Canvas savedSeriesCanvas;

public GraphFXMLController(){
    System.out.println("Graph Initialised");
    GraphicsContext backgroundGC = backgroundCanvas.getGraphicsContext2D();
}

the idea is that I can then call on the graphics context to then draw my series. However, currently when I try and run the application, I can see when the main page loads, it calls for the GraphFXMLController to load as I get the System out message, but I get a null pointer error on my GraphicsContext declaration. It appears that there is a null pointer to backgroundCanvas. Which I do not understand as it should be initialised in the @FXML statement.

Below is the graphFXML file showing that the backgroundCanvas should be there.

<?import javafx.scene.canvas.Canvas?>
<?import javafx.scene.layout.AnchorPane?>

<fx:root id="AnchorPane" minHeight="200.0" minWidth="300.0"     prefHeight="300.0" prefWidth="600.0" type="AnchorPane"     xmlns="http://javafx.com/javafx/9" xmlns:fx="http://javafx.com/fxml/1"     fx:controller="measuremefx.GraphFXMLController">
   <children>
  <Canvas fx:id="backgroundCanvas" height="300.0" width="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
  <Canvas fx:id="series1Canvas" height="300.0" width="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
  <Canvas fx:id="series2Canvas" height="300.0" width="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
  <Canvas fx:id="series3Canvas" height="300.0" layoutX="7.0" width="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
  <Canvas fx:id="series4Canvas" height="300.0" width="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
  <Canvas fx:id="savedSeriesCanvas" height="300.0" layoutY="-1.0" width="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
  </children>
</fx:root>

I haver tried moving the GraphicsContext declaration into an @FXML statement after the backgroundCanvas statement, however it still returns null pointer. Am I loading this incorrectly?

Any help would be much appreciated. I implement several instances of this specific type of graph, all with logarithmic axis and very quick 20Hz refresh rate and without being able to use instances of this code, this project would get messy very quickly.

samp17
  • 547
  • 1
  • 4
  • 16
  • You never seem to load the FXML file. See the example in the [documentation](https://docs.oracle.com/javase/9/docs/api/javafx/fxml/doc-files/introduction_to_fxml.html#custom_components) – James_D May 19 '18 at 13:40
  • Thanks for this. I have just gone through the documentation and I am still confused. I thought the controller was called when the FXML file was loaded (ie within the 4th line of GraphFXML I have fx:controller="measuremefx.GraphFXMLController"> – samp17 May 19 '18 at 14:20
  • I have now moved to the FXMLLoader as the documentation describes ` public GraphFXMLController(){ System.out.println("Graph Initialised"); FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("graphFXML.fxml")); fxmlLoader.setRoot(this); fxmlLoader.setController(this); try { fxmlLoader.load(); } catch (IOException exception) { }` – samp17 May 19 '18 at 14:22
  • 1
    You create an instance of `GraphFXMLController` with the instance declaration in your FXML file: ``. In the original code, you never actually load the FXML. The code in the comments looks correct, assuming you remove `fx:controller = “GraphFXMLController”` from the FXML file. Is it working? – James_D May 19 '18 at 14:32
  • That is it! I had a few attempts at rewriting this and confirm It works completely! Thank you so much for pointing me in the right direction, I am ashamed I did not find this in the documentation myself. – samp17 May 19 '18 at 14:48
  • Found an earlier question related to this as well - I’ll mark this as a duplicate so that this question can redirect there. – James_D May 19 '18 at 14:55

0 Answers0