DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Java
  4. JavaFX Charts Look Pretty Good!

JavaFX Charts Look Pretty Good!

With some tweaking, you can get JavaFX to compile nice-looking charts with a few animations baked in. Configuring them takes a bit of work, but it's worth it.

Dan Newton user avatar by
Dan Newton
·
Feb. 01, 17 · Tutorial
Like (16)
Save
Tweet
Share
31.61K Views

Join the DZone community and get the full member experience.

Join For Free

When I was doing my third-year project in university, I needed a good-looking chart to use in my user interface. Now that I have grown older (nearly two whole years!), I wish I would have turned to the charts in JavaFX rather than the ugly looking JChart2D API that I used. Now don’t get me wrong, I’m not hating on JChart2D, as it was easy to use and, at the time, I was really happy with it. But the charts/graphs that you can use in JavaFX look really good, and they even come with some fancy animations by default. Okay, I’m finished hyping up these graphs, so I’m going to show you a little example application I wrote, and, hopefully, you will feel the same about them as me.

AreaChart

Before you go any further, I recommend that you read up on the basics of using JavaFX. My other post Making Apps with JavaFX covers this.

In this post, I will cover the basic use of a LineChart and AreaChart. The code is pretty simple and only requires a little bit of set up in the Controller and fxml. All you need to do is add the chart to the fxml code, give it a fx:id, set up its properties to your liking, and you're done.

<LineChart fx:id="lineGraph" createSymbols="false" legendVisible="false" prefHeight="372.0" prefWidth="423.0" visible="false">
  <xAxis>
    <NumberAxis autoRanging="false" lowerBound="-10" side="BOTTOM" tickUnit="1" upperBound="10" />
  </xAxis>
  <yAxis>
    <NumberAxis autoRanging="false" lowerBound="-10" side="LEFT" tickUnit="1" upperBound="10" />
  </yAxis>
  <cursor>
    <Cursor fx:constant="CROSSHAIR" />
  </cursor>
</LineChart>


This is this the setup for the LineChart. It defines the axis’ upper and lower bounds, which will change the chart to look more like a graph. By setting the lower bound of the x or y axis, it will cause the chart to auto draw lines to represent the axis that will pass through the coordinates (0,0). The tickUnit represents how separated the grid lines on the chart are and autoRanging has also been disabled to prevent the chart from resizing itself to best fit the content that is on the chart. Finally, a cursor has been defined. There are a few other cursors that can be used, but I think the crosshair cursor looks the best.

Now that you know how to create the chart, you're going to need to know how to plot some points onto it (unless you are happy with it being empty).

public void plotLine() {
    final XYChart.Series<Double, Double> series = new XYChart.Series<Double, Double>();
    for (double x = -range; x <= range; x = x + 0.01) {
        series.getData().add(new XYChart.Data<Double, Double>(x, Math.pow(x, 2)));
    }
    graph.getData().add(series);
}


Here, we are creating a new series object, which will contain the plotted points.

final XYChart.Series<Double, Double> series = new XYChart.Series<Double, Double>()


Then, adding the plots to the series.

series.getData().add(new XYChart.Data<Double, Double>(x, Math.pow(x, 2)))


The plotted coordinates are stored in the form of (x,y), so in the example above, the x coordinate has the value x and the y coordinate takes the value of x^2.

The final step is to add the series to the chart.

graph.getData().add(series)


After this has been done, the points will be plotted onto the chart and will be connected together between each plot. The order that the plots (XYChart.Data) are added to the series does not matter, as it will connect the closest plots to each other, although this isn’t relevant if you're adding plots from loops.

Now that the basics of using these charts is covered, let's get onto a larger example. Below is all the code you need to get set up.

public class MainAppLauncher extends Application {

    public static void main(String[] args) {
        Application.launch(MainAppLauncher.class, args);
    }

    @Override
    public void start(Stage stage) throws Exception {
        try {
            Parent root = FXMLLoader.load(getClass().getClassLoader()
            .getResource("lankydan/tutorials/fxml/MainApp.fxml"));
            stage.setScene(new Scene(root));
            stage.setTitle("JavaFX Graph Example");
            stage.show();
        } catch (Exception e) {
            System.out.print(e);
        }
    }
}


MainAppLauncher is used to load run the JavaFX code.

public class MainAppController implements Initializable {

    @FXML
    private LineChart<Double, Double> lineGraph;

    @FXML
    private AreaChart<Double, Double> areaGraph;

    @FXML
    private Button lineGraphButton;

    @FXML
    private Button areaGraphButton;

    @FXML
    private Button xyButton;

    @FXML
    private Button xyButton2;

    @FXML
    private Button squaredButton;

    @FXML
    private Button squaredButton2;

    @FXML
    private Button cubedButton;

    @FXML
    private Button cubedButton2;

    @FXML
    private Button clearButton;

    private MyGraph mathsGraph;
    private MyGraph areaMathsGraph;

    @Override
    public void initialize(final URL url, final ResourceBundle rb) {
        mathsGraph = new MyGraph(lineGraph, 10);
        areaMathsGraph = new MyGraph(areaGraph, 10);
    }

    @FXML
    private void handleLineGraphButtonAction(final ActionEvent event) {
        lineGraph.setVisible(true);
        areaGraph.setVisible(false);
    }

    @FXML
    private void handleAreaGraphButtonAction(final ActionEvent event) {
        areaGraph.setVisible(true);
        lineGraph.setVisible(false);
    }

    @FXML
    private void handleXYButtonAction(final ActionEvent event) {
        plotLine(x -> x);
    }

    private void plotLine(Function<Double, Double> function) {
        if (lineGraph.isVisible()) {
            mathsGraph.plotLine(function);
        } else {
            areaMathsGraph.plotLine(function);
        }
    }

    @FXML
    private void handleXYButton2Action(final ActionEvent event) {
        plotLine(x -> x - 3);
    }

    @FXML
    private void handleSquaredButtonAction(final ActionEvent event) {
        plotLine(x -> Math.pow(x, 2));
    }

    @FXML
    private void handleSquaredButton2Action(final ActionEvent event) {
        plotLine(x -> Math.pow(x, 2) + 2);
    }

    @FXML
    private void handleCubedButtonAction(final ActionEvent event) {
        plotLine(x -> Math.pow(x, 3));
    }

    @FXML
    private void handleCubedButton2Action(final ActionEvent event) {
        plotLine(x -> Math.pow(x - 3, 3) - 1);
    }

    @FXML
    private void handleClearButtonAction(final ActionEvent event) {
        clear();
    }

    private void clear() {
        if (lineGraph.isVisible()) {
            mathsGraph.clear();
        } else {
            areaMathsGraph.clear();
        }
    }
}


MainAppController is the centerpiece of this code, which controls the chart by handling the ActionEvents fired by button presses, which, in this example, leads to the lines being drawn onto the chart. I have also used some lambda expressions in this example to make the code shorter and a little bit fancier.

public class MyGraph {

    private XYChart<Double, Double> graph;
    private double range;

    public MyGraph(final XYChart<Double, Double> graph, final double range) {
        this.graph = graph;
        this.range = range;
    }

    public void plotLine(final Function<Double, Double> function) {
        final XYChart.Series<Double, Double> series = new XYChart.Series<Double, Double>();
        for (double x = -range; x <= range; x = x + 0.01) {
            plotPoint(x, function.apply(x), series);
        }
        graph.getData().add(series);
    }

    private void plotPoint(final double x, final double y,
    final XYChart.Series<Double, Double> series) {
        series.getData().add(new XYChart.Data<Double, Double>(x, y));
    }

    public void clear() {
        graph.getData().clear();
    }
}


MyGraph is a wrapper object that takes in an XYChart, which both LineChart and AreaChart are, and has some methods to plot and clear lines. This code could be in MainAppController, as there isn’t much code in it, but it looks much tidier like this. It uses the plotLine code shown above, although it is split out into a two methods instead.

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.Cursor?>
<?import javafx.scene.chart.AreaChart?>
<?import javafx.scene.chart.LineChart?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>

<AnchorPane maxHeight="400.0" maxWidth="600.0" minHeight="400.0" minWidth="600.0" prefHeight="400.0" prefWidth="600.0" styleClass="root" stylesheets="lankydan/tutorials/fxml/css.css" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="lankydan.tutorials.fxml.controller.MainAppController">
   <children>
    <AnchorPane layoutX="416.0" minHeight="0.0" minWidth="0.0" prefHeight="400.0" prefWidth="185.0">
         <children>
            <VBox prefHeight="398.0" prefWidth="183.0">
               <children>
                  <Button mnemonicParsing="false" onAction="#handleXYButtonAction" prefHeight="66.0" prefWidth="193.0" text="y=x" fx:id="xyButton" />
                  <Button mnemonicParsing="false" onAction="#handleXYButton2Action" prefHeight="66.0" prefWidth="207.0" text="y=x-3" fx:id="xyButton2" />
                  <Button fx:id="squaredButton" mnemonicParsing="false" onAction="#handleSquaredButtonAction" prefHeight="67.0" prefWidth="220.0" text="y=x^2" />
                  <Button fx:id="squaredButton2" mnemonicParsing="false" onAction="#handleSquaredButton2Action" prefHeight="67.0" prefWidth="232.0" text="y=x^2+2" />
                  <Button fx:id="cubedButton" mnemonicParsing="false" onAction="#handleCubedButtonAction" prefHeight="67.0" prefWidth="236.0" text="y=x^3" />
                  <Button fx:id="cubedButton2" mnemonicParsing="false" onAction="#handleCubedButton2Action" prefHeight="67.0" prefWidth="266.0" text="y=(x-3)^3-1" />
                  <Button fx:id="clearButton" mnemonicParsing="false" onAction="#handleClearButtonAction" prefHeight="67.0" prefWidth="266.0" text="clear" />
               </children>
            </VBox>
         </children>
      </AnchorPane>
      <AnchorPane layoutX="-7.0" prefHeight="400.0" prefWidth="420.0">
         <children>
            <LineChart fx:id="lineGraph" legendVisible="false" prefHeight="372.0" prefWidth="423.0" visible="false">
              <xAxis>
              <NumberAxis autoRanging="false" lowerBound="-10" side="BOTTOM" tickUnit="1" upperBound="10" />
              </xAxis>
              <yAxis>
                <NumberAxis autoRanging="false" lowerBound="-10" side="LEFT" tickUnit="1" upperBound="10" />
              </yAxis>
              <cursor>
                 <Cursor fx:constant="CROSSHAIR" />
              </cursor>
            </LineChart>
            <AreaChart fx:id="areaGraph" legendVisible="false" prefHeight="372.0" prefWidth="423.0">
              <xAxis>
              <NumberAxis autoRanging="false" lowerBound="-10" side="BOTTOM" tickUnit="1" upperBound="10" />
              </xAxis>
              <yAxis>
                <NumberAxis autoRanging="false" lowerBound="-10" side="LEFT" tickUnit="1" upperBound="10" />
              </yAxis>
              <cursor>
                 <Cursor fx:constant="CROSSHAIR" />
              </cursor>
            </AreaChart>
            <Button fx:id="lineGraphButton" onAction="#handleLineGraphButtonAction" layoutX="35.0" layoutY="366.0" mnemonicParsing="false" prefHeight="29.0" prefWidth="185.0" text="Line Graph" />
            <Button layoutX="224.0" onAction="#handleAreaGraphButtonAction" layoutY="366.0" mnemonicParsing="false" prefHeight="29.0" prefWidth="185.0" text="Area Graph" />
         </children>
      </AnchorPane>
   </children>
</AnchorPane>


MainApp.fxml contains all the code to set up the visuals of the application.

#pane, .root{
    -fx-background-color: #353434;
    -fx-foreground-color: #353434;
}

.default-color0.chart-series-line { -fx-stroke: #2c2e4e; }

.chart-vertical-zero-line {
    -fx-stroke: white;
}
.chart-horizontal-zero-line {
    -fx-stroke: white;
}

.chart-plot-background {
    -fx-background-color: #575758;
    -fx-foreground-color: white;
    -fx-stroke: white;
} 

.chart-vertical-grid-lines {
    -fx-stroke: #898887;
}
.chart-horizontal-grid-lines {
    -fx-stroke: #898887;
}
.chart-alternative-row-fill {
    -fx-fill: transparent;
    -fx-stroke: transparent;
    -fx-stroke-width: 0;
}

.axis {
-fx-stroke: white;
-fx-fill: white;
    -fx-font-size: 1.4em;    
    -fx-tick-label-fill: white;
    -fx-font-family: Tahoma;
    -fx-tick-length: 0;
    -fx-minor-tick-length: 0;
}

.background {
    -fx-background-color: #674A44;
    -fx-foreground-color: #353434;
}

.button {
    -fx-padding: 5 22 5 22;   
    -fx-border-color: #353434;
    -fx-border-width: 0;
    -fx-background-radius: 0;
    -fx-background-color: derive(#353434,20%);
    -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
    -fx-font-size: 11pt;
    -fx-text-fill: #d8d8d8;
    -fx-background-insets: 0 0 0 0, 0, 1, 2;
}

.button:hover {
    -fx-background-color: #3a3a3a;
}

.button:pressed, .button:default:hover:pressed {
  -fx-background-color: #bdbcbc;
  -fx-text-fill: black;
}

.button:disabled, .button:default:disabled {
    -fx-opacity: 0.4;
    -fx-background-color: #353434;
    -fx-text-fill: white;
}

.button:default {
    -fx-background-color: -fx-focus-color;
    -fx-text-fill: #ffffff;
}

.button:default:hover {
    -fx-background-color: derive(-fx-focus-color,30%);
}

.text-area .content {
-fx-background-color: #575758;
}


Css.css is the stylesheet that is used for this application and is referenced inside MainApp.fxml.

If you try to run this by yourself, there are a few changes you will need to remember to make. Otherwise, you are going to run into some errors. Make sure that you have your files structured well or understand how the paths to the files work. Below is a picture of how I structured my files.

Project Structure

Once you have sorted out your project structure, remember to rename the paths in your code to match the structure and names you have chosen. This includes the path in MainAppLauncher and the paths to controller and css in MainApp.fxml.

Once everything has been put together correctly, you should be able to run it, and it will look like this.

Area Chart

And by pressing the Line Graph button, it will look like this.

Line Chart

So now that you have seen a little bit of what the charts in JavaFX have to offer, you might consider using it when your writing some of your own applications. They are pretty easy to set up and come with some decent styles and animations by default. I will write a few more posts on how to use some of the other charts in the future.

Chart JavaFX

Published at DZone with permission of Dan Newton, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Introduction To OpenSSH
  • What Is Advertised Kafka Address?
  • How We Solved an OOM Issue in TiDB with GOMEMLIMIT
  • Implementing PEG in Java

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: