Cannot update scatter chart NumberAxis metadata in JavaFX with FXML - javafx

I'm trying to follow this guide:
https://docs.oracle.com/javafx/2/charts/scatter-chart.htm
Guide's examples works fine but when trying to combine this with scene builder and FXML, I get some strange errors. First of all defaulted X axis as category axis and had to manually change it in the XML. Now the snippet looks like this:
<ScatterChart fx:id="modScatChart" layoutX="303.0" layoutY="122.0" title="Modify timeline" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<xAxis>
<NumberAxis fx:id="scatXAxis" side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis fx:id="scatYAxis" side="LEFT" />
</yAxis>
</ScatterChart>
The X axis need to be NumberAxis because the X values will be floating point date values normalized like 180th day of 2018 is appr. 2018,5. Now when trying to give the min, max, tick values to NumberAxis it seems that everything is ignored. It looks always like this:
enter image description here
Here is the relevant code snippet (GraphBean basically provides HashMap (milliseconds/loc where ms will be converted to date), minimum value for x axis and maximum value for y axis.
public class FXMLDocumentController implements Initializable {
#FXML NumberAxis scatXAxis = new NumberAxis();
#FXML NumberAxis scatYAxis = new NumberAxis();
#FXML ScatterChart modScatChart = new ScatterChart(scatXAxis,scatYAxis);
#Override
public void initialize(URL url, ResourceBundle rb) {
try {
setupGraphs();
} catch(SQLException e){
System.out.println(e);
} catch(Exception e){
System.out.println(e);
}
}
private void setupGraphs() throws SQLException {
Calendar cal = Calendar.getInstance();
cal.setTime(new Date(graphsBean.getMinModMs()));
int minYear = cal.get(Calendar.YEAR);
cal.setTime(new Date(System.currentTimeMillis()));
int thisYear = cal.get(Calendar.YEAR);
scatXAxis = new NumberAxis(minYear, thisYear, 1);
scatYAxis = new NumberAxis(0, graphsBean.getMaxLoc(), Math.round(graphsBean.getMaxLoc()/10));
scatXAxis.setLabel("Time");
scatYAxis.setLabel("Application size in loc");
Series series1 = new XYChart.Series();
series1.setName("All");
HashMap<Long, Integer> scatMap = graphsBean.getScatMap();
for (Map.Entry<Long, Integer> entry : scatMap.entrySet()) {
cal.setTime(new Date(entry.getKey()));
int curYear = cal.get(Calendar.YEAR);
float yearDayFlt = curYear+(float)(cal.get(Calendar.DAY_OF_YEAR))/365;
series1.getData().add(new XYChart.Data(yearDayFlt, entry.getValue()));
}
modScatChart.getData().addAll(series1);
}
}
Have also tried to fully initialize the NumberAxis with manual data at the start of the class, but still the same result. What the heck I'm missing here?

Related

JavaFX multi linechart and series - curves missing

I would like to plot one serie per chart, and in the end all the series together in one chart, but did not succeed. I'm asking for help. The code is simple and straight forward. Here is my code:
The main class;
public class TestChart extends Application {
GenPlots genPlots =new GenPlots();
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(event -> {
genPlots.GenPlots("Hello");
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 200, 250);
primaryStage.setTitle("TestCharts");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
And the class aimed to generate the series and the charts:
public class GenPlots {
public GenPlots() {};
Axis xAxis = new NumberAxis();
Axis yAxis = new NumberAxis();
LineChart<Number, Number> lineChart = new LineChart<Number, Number>
(xAxis, yAxis);
LineChart<Number, Number> lineChartMulti = new LineChart<Number,
Number>(xAxis, yAxis);
String serName="*";
// generate the linecharts
public void GenPlots (String hello) {
lineChart.getData().clear();
lineChartMulti.getData().clear();
for (int j=1; j<4;j++) {
XYChart.Series serSIF = new XYChart.Series();
serSIF=getSeries();
serName=String.valueOf(j);
serSIF.setName("Only one "+serName);
lineChart.getData().add(serSIF);
displayChart(lineChart,serName);
lineChartMulti.getData().add(serSIF);
}
displayChart(lineChartMulti,serName+"All Series");
} // end method
// get the series with values - sample
public XYChart.Series getSeries()
{
double x=0.0;
double fx=0.0;
XYChart.Series serL = new XYChart.Series();
for (int k=1; k<5;k++)
{
x=x+2;
fx=x*x*j;
serL.getData().add(new XYChart.Data(x,fx));
}
return serL;
}
// plot the lineCharts
public void displayChart( LineChart<Number, Number>lineChart, String
chartTitle )
{
Stage window = new Stage();
window.initModality(Modality.NONE);
StackPane vb = new StackPane();
vb.setPadding(new Insets(20, 20, 20, 20));
lineChart.setTitle(chartTitle);
vb.getChildren().add(lineChart);
Scene scene = new Scene(vb,500,600);
window.setScene(scene);
window.show();
}
}
Also, the last plots with all series are showing correctly, but the other ones , - one serie per chart - are distorted , or not plotted at all. It seems that the series are resetted to null each time a linechart is generated. I thinks is due to the that series are observable, but I can not figure out how to resolve this problem. Ask kindly for your contribution
I found the solution, which could be usefull for other people:
save the series in a ObservableList-
ObservableList<XYChart.Series<Number,Number>> ser = FXCollections.observableArrayList();
If needed do not clear the series itself, rather the ObservableList.

Reload and display blank JavaFX BarChart after launching the program

I'm working on a JavaFX program that can display different grocery items as a BarChart. My code structure uses an initial loading class, GroceryGrapher.java, a controller, GroceryGrapherController.java, and an .fxml file, GroceryGrapher.fxml.
I'm relatively new to JavaFX and I've tried different approaches to creating and displaying my BarChart. For the most part I've resolved issues and can load the chart with its data and components, but my issue is that the chart won't display / update after the program has started. The BarChart is part of an AnchorPane (which all comes under a StackPane) in my FXML file, and I've tried to update it after initialization since it starts out blank but it remains blank.
The BarChart is part of the AnchorPane, which also has a MenuBar. My goal was to be able to update different parts of the BarChart via the MenuBar. When I load the program, the MenuBar and empty BarChart are loaded:
This is how I initialize my BarChart and its data:
public class GroceryGrapherController implements Initializable {
#FXML
private AnchorPane anchorPane = new AnchorPane();
#FXML
private NumberAxis xAxis = new NumberAxis();
#FXML
private CategoryAxis yAxis = new CategoryAxis();
#FXML
private BarChart<Number, String> groceryBarChart;
#FXML
//private Stage stage;
Parent root;
#Override
public void initialize(URL location, ResourceBundle resources) {
groceryBarChart = new BarChart<Number, String>(xAxis, yAxis);
groceryBarChart.setTitle("Horizontal Grocery List");
xAxis.setLabel("Number");
xAxis.setTickLabelRotation(90);
yAxis.setLabel("Grocery Type");
// Populate BarChart
XYChart.Series<Number, String> series1 = new XYChart.Series<>();
series1.setName("Chocolates");
series1.getData().add(new XYChart.Data<Number, String>(25601.34, "Reese"));
series1.getData().add(new XYChart.Data<Number, String>(20148.82, "Cadbury"));
series1.getData().add(new XYChart.Data<Number, String>(10000, "Mars"));
series1.getData().add(new XYChart.Data<Number, String>(35407.15, "Snickers"));
series1.getData().add(new XYChart.Data<Number, String>(12000, "Lindt"));
XYChart.Series<Number, String> series2 = new XYChart.Series<>();
series2.setName("Vegetables");
series2.getData().add(new XYChart.Data<Number, String>(57401.85, "Cabbage"));
series2.getData().add(new XYChart.Data<Number, String>(41941.19, "Lettuce"));
series2.getData().add(new XYChart.Data<Number, String>(45263.37, "Tomato"));
series2.getData().add(new XYChart.Data<Number, String>(117320.16, "Eggplant"));
series2.getData().add(new XYChart.Data<Number, String>(14845.27, "Beetroot"));
XYChart.Series<Number, String> series3 = new XYChart.Series<>();
series3.setName("Drinks");
series3.getData().add(new XYChart.Data<Number, String>(45000.65, "Water"));
series3.getData().add(new XYChart.Data<Number, String>(44835.76, "Coke"));
series3.getData().add(new XYChart.Data<Number, String>(18722.18, "Sprite"));
series3.getData().add(new XYChart.Data<Number, String>(17557.31, "Mountain Dew"));
series3.getData().add(new XYChart.Data<Number, String>(92633.68, "Beer"));
// Need to do this because of a bug with CategoryAxis... manually set categories
yAxis.setCategories(FXCollections.observableArrayList("Reese", "Cadbury", "Mars", "Snickers", "Lindt", "Cabbage", "Lettuce",
"Tomato", "Eggplant", "Beetroot", "Water", "Coke", "Sprite", "Mountain Dew", "Beer"));
groceryBarChart.getData().addAll(series1, series2, series3);
}
Now, I have an item:
In my MenuBar to load the BarChart (since it initially appears empty), which utilizes the a method I created, loadGraph. I've tried different methods to get my updated BarChart to show up with no major success. My initial idea was to use setScene in the loadGraph method to set the scene to the BarChart, as shown in this example.
Stage stage = (Stage) anchorPane.getScene().getWindow();
stage.setScene(new Scene(groceryBarChart, 800, 600));
While that did work, it naturally replaced the scene to solely consist of the BarChart, removing my MenuBar and previous GUI:
I saw this question which had a similar issue (I think) but I wasn't really sure how to apply it to my program. I got this code:
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("GroceryGrapher.fxml"));
root = (Parent) fxmlLoader.load();
GroceryGrapherController ggController = fxmlLoader.getController();
} catch (Exception e) {
e.printStackTrace();
}
I copied over my initialization from the initialize method to a new method and tried calling it with ggController but it didn't change anything. I also tried creating a new method, updateData:
#FXML
private void updateData() {
Platform.runLater(() -> {
groceryBarChart.setTitle("Horizontal Grocery List");
});
}
I tried calling it with ggController in the loadGraph method but my BarChart still didn't change.
I know I'm doing something wrong, but I'm not very familiar with the BarChart class and JavaFX in too much depth, so I figured I would come here to ask for help. I need to update the existing BarChart in my GUI so that my data can load without replacing the entire scene, in order for the MenuBar to remain visible in the scene able to perform functions. How would I go about altering the BarChart component part of my AnchorPane, updating the data as necessary, without completely replacing the scene as I did previously?
Thank you very much and sorry if this question is bad or my coding practices are wrong. All feedback would be much appreciated!
EDIT
Just in case it might help, here is my FXML code for the BarChart.
<BarChart fx:id="groceryBarChart" layoutY="31.0" prefHeight="566.0" prefWidth="802.0">
<xAxis>
<CategoryAxis fx:id="yAxis" side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" fx:id="xAxis" />
</yAxis>
</BarChart>
Most of the problems seem to be resolved, so I'll comment on this part only:
Ohh, I see. I increased the height and it seems to be thicker, thank you for your comment, that's very interesting behavior from the library. So I shouldn't use the approach I'm using?
Well, it depends. There is no other way to treat each data series as a separate list of categories in BarChart (at least, I'm not aware of any), so if you want to have groups of categories, each group with its own color, you'll have to use the same method as in your example.
The alternatives are to write your own implementation of bar chart layoutPlotChildren() method which will show only one bar per category (the only one if there are no two data series that contain the same category), or to find another chart library with those features. Of couse, you can always choose to show your data in different format (one data series for each month).
If you plan to use your original method, you'll have to adjust vertical position of bars (to center them again), and you'll have to fix bar height problem too:
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.chart.*;
import java.net.URL;
import java.util.ResourceBundle;
public class GroceryGrapherController implements Initializable {
#FXML
private NumberAxis xAxis;
#FXML
private CategoryAxis yAxis;
#FXML
private BarChart<Number, String> groceryBarChart;
//the height of one bar in the chart (if it can fit category area); you can pick another value
private final double barHeight = 10;
#Override
public void initialize(URL location, ResourceBundle resources) {
groceryBarChart.setTitle("Horizontal Grocery List");
//there's only one bar per category, so remove the gap (don't edit this, it's used in calculations)
groceryBarChart.setBarGap(0);
//gap between categories; you can pick another value
groceryBarChart.setCategoryGap(5);
//there're some bugs with the chart layout not updating when this is enabled, so disable it
groceryBarChart.setAnimated(false);
xAxis.setLabel("Number");
xAxis.setTickLabelRotation(90);
yAxis.setLabel("Grocery Type");
//if category height changes (ex. when you resize the chart), bar positions and height need to be recalculated
yAxis.categorySpacingProperty().addListener((observable, oldValue, newValue) ->
recalculateBarPositionsAndHeight()
);
//your original data
// Populate BarChart
final XYChart.Series<Number, String> series1 = new XYChart.Series<>();
series1.setName("Chocolates");
series1.getData().addAll(
new XYChart.Data<>(25601.34, "Reese"),
new XYChart.Data<>(20148.82, "Cadbury"),
new XYChart.Data<>(10000, "Mars"),
new XYChart.Data<>(35407.15, "Snickers"),
new XYChart.Data<>(12000, "Lindt")
);
final XYChart.Series<Number, String> series2 = new XYChart.Series<>();
series2.setName("Vegetables");
series2.getData().addAll(
new XYChart.Data<>(57401.85, "Cabbage"),
new XYChart.Data<>(41941.19, "Lettuce"),
new XYChart.Data<>(45263.37, "Tomato"),
new XYChart.Data<>(117320.16, "Eggplant"),
new XYChart.Data<>(14845.27, "Beetroot")
);
final XYChart.Series<Number, String> series3 = new XYChart.Series<>();
series3.setName("Drinks");
series3.getData().addAll(
new XYChart.Data<>(45000.65, "Water"),
new XYChart.Data<>(44835.76, "Coke"),
new XYChart.Data<>(18722.18, "Sprite"),
new XYChart.Data<>(17557.31, "Mountain Dew"),
new XYChart.Data<>(92633.68, "Beer")
);
groceryBarChart.getData().addAll(series1, series2, series3);
}
private void recalculateBarPositionsAndHeight() {
final int seriesCount = groceryBarChart.getData().size();
final double categoryHeight = yAxis.getCategorySpacing() - groceryBarChart.getCategoryGap();
final double originalBarHeight = categoryHeight / seriesCount;
final double barTranslateY = originalBarHeight * (seriesCount - 1) / 2;
//find the (bar) node associated with each series data and adjust its size and position
groceryBarChart.getData().forEach(numberStringSeries ->
numberStringSeries.getData().forEach(numberStringData -> {
Node node = numberStringData.getNode();
//calculate the vertical scaling of the node, so that its height matches "barHeight"
//or maximum height available if it's too big
double scaleY = Math.min(barHeight, categoryHeight) / originalBarHeight;
node.setScaleY(scaleY);
//translate the node vertically to center it around the category label
node.setTranslateY(barTranslateY);
})
);
}
}
I chose fixed bar height, but you don't need to (in which case you'll have to edit recalculateBarPositionsAndHeight method to use originalBarHeight).

How to set DateAxis TickUnit (Date Interval) in JavaFX?

I'm developing a chart using JavaFX. I needed DateAxis, so I use
DateAxis from this code. The result is this. But I want show 2 date
interval, I didn't find solution. The result always show one day interval. This is my code.
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
ObservableList<XYChart.Series<Date, Number>> series = FXCollections.observableArrayList();
ObservableList<XYChart.Data<Date, Number>> series1Data = FXCollections.observableArrayList();
series1Data.add(new XYChart.Data<Date, Number>(new GregorianCalendar(2018,1, 12).getTime(), 2));
series1Data.add(new XYChart.Data<Date, Number>(new GregorianCalendar(2018, 1, 15).getTime(), 3));
series1Data.add(new XYChart.Data<Date, Number>(new GregorianCalendar(2018, 1, 16).getTime(), 4));
series.add(new XYChart.Series<>("Series1", series1Data));
NumberAxis numberAxis = new NumberAxis();
DateAxis dateAxis = new DateAxis();
dateAxis.setAutoRanging(false);
dateAxis.setLowerBound(new GregorianCalendar(2018,2, 12).getTime());
dateAxis.setUpperBound(new GregorianCalendar(2018,2, 16).getTime());
dateAxis.setTickLabelFormatter(new StringConverter<Date>() {
#Override
public String toString(Date object) {
return simpleDateFormat.format(object);
}
#Override
public Date fromString(String string) {
return null;
}
});
LineChart lineChart = new LineChart(dateAxis, numberAxis, series);
Scene scene = new Scene(lineChart, 900, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
How can I do that?
I might be wrong, but it seems that DateAxis API does not allow you to specify a preferred displayed interval, so you need to change it yourself.
In DateAxis class there's an Interval enum. If you change this line:
DAY(Calendar.DATE, 1),
To this one:
DAY(Calendar.DATE, 2),
And comment other intervals as necessary...
// HOUR_12(Calendar.HOUR, 12),
2-day intervals will be shown. Keep in mind that doing this is contrary to the author's intent, who wrote it this way so that the intervals are adjusted automatically, and it's a very crude solution. For a better one, you'd have to rewrite the calculateTickValues method, as well as change the private enum to some kind of public API.

How to make negative values in stacked bar chart start at previous stack?

I want to visualize incoming vs outgoing amounts per month in a stacked bar chart, but also the difference should be immediately visible.
I'm using the following sample code:
public class Statistics extends Application {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
final StackedBarChart<String, Number> sbc = new StackedBarChart<>(xAxis, yAxis);
final XYChart.Series<String, Number> incoming = new XYChart.Series<>();
final XYChart.Series<String, Number> outgoing = new XYChart.Series<>();
#Override
public void start(Stage stage) {
xAxis.setLabel("Month");
xAxis.setCategories(FXCollections.observableArrayList(
Arrays.asList("Jan", "Feb", "Mar")));
yAxis.setLabel("Value");
incoming.setName("Incoming");
incoming.getData().add(new XYChart.Data("Jan", 25601.34));
incoming.getData().add(new XYChart.Data("Feb2", 20148.82));
incoming.getData().add(new XYChart.Data("Mar2", 10000));
outgoing.setName("Outgoing");
outgoing.getData().add(new XYChart.Data("Jan", -7401.85));
outgoing.getData().add(new XYChart.Data("Feb2", -1941.19));
outgoing.getData().add(new XYChart.Data("Mar2", -5263.37));
Scene scene = new Scene(sbc, 800, 600);
sbc.getData().addAll(incoming, outgoing);
stage.setScene(scene);
stage.show();
}
}
Which results in:
As you can see, the negative outgoing values are displayed below zero instead of being subtracted from the positive incoming values, which makes it hard to see the delta between the two.
What I want instead is that a bar for a negative value starts at the top of a bar for a positive value, but as they would overlap then, also apply an offset along the x-axis. At the example of the "Jan" series this should look similar to:
I was playing around with getNode().setTranslateX/Y(), but that does not seem to be a good solution as the translation units is not ticks in the chart, but something else.
How can I create a "stacked" bar chart like in the second image in an elegant way?
Your question is really cool!
So, here I want to share my solution using a BarChart instead of a StackedBarChart. It might be a bit hacky, but it is the only one that worked for me.
The first thing that came to my mind was to just simply take the bar and change its Y coordinate, but unfortunately we cannot access bars directly. So after digging through the source code of XYChart I found that all of the chart's content are located in Group plotContent, but the getter for that is protected. In this situation the one of things to do is to extend the class and increase the scope of method. So (finally), here the code comes:
public class Statistics extends Application {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
// here I use the modified version of chart
final ModifiedBarChart<String, Number> chart = new ModifiedBarChart<>(xAxis, yAxis);
final XYChart.Series<String, Number> incoming = new XYChart.Series<>();
final XYChart.Series<String, Number> outgoing = new XYChart.Series<>();
#Override
public void start(Stage stage) {
xAxis.setLabel("Month");
xAxis.setCategories(FXCollections.observableArrayList(
Arrays.asList("Jan", "Feb", "Mar")));
yAxis.setLabel("Value");
incoming.setName("Incoming");
incoming.getData().add(new XYChart.Data("Jan", 25601.34));
incoming.getData().add(new XYChart.Data("Feb", 20148.82));
incoming.getData().add(new XYChart.Data("Mar", 10000));
outgoing.setName("Outgoing");
// To set the min value of yAxis you can either set lowerBound to 0 or don't use negative numbers here
outgoing.getData().add(new XYChart.Data("Jan", -7401.85));
outgoing.getData().add(new XYChart.Data("Feb", -1941.19));
outgoing.getData().add(new XYChart.Data("Mar", -5263.37));
chart.getData().addAll(incoming, outgoing);
Scene scene = new Scene(chart, 800, 600);
stage.setScene(scene);
stage.show();
// and here I iterate over all plotChildren elements
// note, that firstly all positiveBars come, then all negative ones
int dataSize = incoming.getData().size();
for (int i = 0; i < dataSize; i++) {
Node positiveBar = chart.getPlotChildren().get(i);
// subtract 1 to make two bars y-axis aligned
chart.getPlotChildren().get(i + dataSize).setLayoutY(positiveBar.getLayoutY() - 1);
}
}
// note, that I extend BarChart here
private static class ModifiedBarChart<X, Y> extends BarChart<X, Y> {
public ModifiedBarChart(#NamedArg("xAxis") Axis<X> xAxis, #NamedArg("yAxis") Axis<Y> yAxis) {
super(xAxis, yAxis);
}
public ModifiedBarChart(#NamedArg("xAxis") Axis<X> xAxis, #NamedArg("yAxis") Axis<Y> yAxis, #NamedArg("data") ObservableList<Series<X, Y>> data) {
super(xAxis, yAxis, data);
}
public ModifiedBarChart(#NamedArg("xAxis") Axis<X> xAxis, #NamedArg("yAxis") Axis<Y> yAxis, #NamedArg("data") ObservableList<Series<X, Y>> data, #NamedArg("categoryGap") double categoryGap) {
super(xAxis, yAxis, data, categoryGap);
}
#Override
public ObservableList<Node> getPlotChildren() {
return super.getPlotChildren();
}
}
The result:

BarChart/LineChart - Only the label for the last data point showing

I am not able to get a bar chart or a line chart to show all the labels on the X axis.As you can see in the provided print screen, only the latest datapoint shows its label.
This is when using scene builder. Do I have to have an ObservableList with Strings for the CategoriesAxis ?
I have made a MVCE of my current code:
#FXML
LineChart<String, Integer> lineChart;
#FXML
private Label label;
#FXML
private void handleButtonAction(ActionEvent event) {
XYChart.Series series1 = new XYChart.Series();
series1.getData().add(new XYChart.Data<String, Integer>("austria", 300));
series1.getData().add(new XYChart.Data<String, Integer>("rr", 400));
series1.getData().add(new XYChart.Data<String, Integer>("qq", 3003));
lineChart.getData().addAll(series1);
With your approach, every time you click the button a new series is added to the chart, always with the same data. Note that for the second series you'll get all the labels...
In orther to have just one series, with proper labels, as you've already pointed out, create a global ObservableList, add it at the beginning to the series, and then on Initialize add the series to the chart.
Then if you want to process click events, just add new values to the series, and you'll see the new labels will be added too.
#FXML private LineChart<String, Integer> lineChart;
private final ObservableList<XYChart.Data<String,Integer>> xyList1 =
FXCollections.observableArrayList(
new XYChart.Data<>("austria", 300),
new XYChart.Data<>("rr", 400),
new XYChart.Data<>("qq", 3003));
private final XYChart.Series series1 = new XYChart.Series(xyList1);
#Override
public void initialize(URL url, ResourceBundle rb) {
series1.setName("Name");
lineChart.getData().addAll(series1);
}
#FXML
public void handleButtonAction(ActionEvent e){
// sample new points
series1.getData().add(new XYChart.Data<>("Point "+(series1.getData().size()+1),
new Random().nextInt(3000)));
}
EDIT
If you want to add new series every time you click the button, you can do it as you proposed initially.
But note it seems to be a bug in the first series, and only the last label is shown, IF the chart is animated. To avoid this bug (consider filing it to JIRA), just disable animation and it will work.
If you want animation, you can add a listener to the chart, and set animation once it has at least one series. This will work:
#FXML private LineChart<String, Integer> lineChart;
#Override
public void initialize(URL url, ResourceBundle rb) {
// Workaround to allow animation after series is added
lineChart.getData().addListener(
(ListChangeListener.Change<? extends XYChart.Series<String, Integer>> c) -> {
lineChart.setAnimated(c.getList().size()>1);
});
}
#FXML
public void handleButtonAction(ActionEvent e){
XYChart.Series series1 = new XYChart.Series();
series1.setName("Series "+lineChart.getData().size() );
lineChart.getData().addAll(series1);
series1.getData().add(new XYChart.Data<>("Point "+(series1.getData().size()+1),
new Random().nextInt(3000)));
series1.getData().add(new XYChart.Data<>("Point "+(series1.getData().size()+1),
new Random().nextInt(3000)));
series1.getData().add(new XYChart.Data<>("Point "+(series1.getData().size()+1),
new Random().nextInt(3000)));
}
This is a bug in j8_u20. With the help of the people on the Java Chat we have tested with u20,u25 and u11. This is the result:
U25 bug is visible
U20 bug is visible
U11 bug is not visible
Bug filed to JAVA jira.

Resources