Reputation: 4208
I use a JavaFX LineChart which takes nearly a second to be rendered and I want to move a vertical marker (cursor position) over this chart which is updated very frequently.
I tested the following solutions:
However, these solutions are not made for frequent marker updates on big charts. Whenever a marker changes the whole chart is redrawn.
How can I prevent Java from redrawing the whole chart when the marker gets updated? I thought of a wrapper component that can be put around the chart and caches the chart image as long as the chart content and chart boundaries do not change. Does something like that exist or how could one implement this?
Upvotes: 0
Views: 1242
Reputation: 51
As mentioned by Stefan the best way is to set the cache
property to true
of the nodes that you do not want to update. What I want to add is that the StackPane
is not strickly necessary, it could be a Pane
or another region (actually a StackPane has its own way to add new children which make you dificult to manipulate).
Here is a snippet of the code I used to implement a LineMarker that moves along the LineChart
(in my example I had a video and the LineMarker followed the time in the chart)
Main Example
Pane pane;
LineChart chart;
MediaPlayer mediaPlayer;
// Create pane, chart (and mediaplayer for this example)
pane.getChildren.add(chart);
List<XYChart.Data<Number, Number> dataList;
XYChart.Series series = new XYChart.Series();
// Fill the data
ObservableList<XYChart.Data<Number, Number>> list;
list = FXCollections.observableList(dataList);
series.setData(list);
chart.getData().add(series);
chart.setCache(true);
LineMarker lineMarker = new LineMarker(pane, (ValueAxis) chart.getXAxis(), 0, (ValueAxis) chart.getYAxis());
mediaPlayer.currentTimeProperty().addListener((v, oldValue, newValue) -> lineMarker.updateMarker(newValue.doubleValue()));
LineMarker.java
public class LineMarker extends Line{
private final DoubleProperty value = new SimpleDoubleProperty();
private Pane pane;
private ValueAxis xAxis;
private ValueAxis yAxis;
public LineMarker(Pane pane, ValueAxis xAxis, double value, ValueAxis yAxis){
this.pane = pane;
this.xAxis = xAxis;
this.yAxis = yAxis;
Number lowerY = yAxis.toRealValue(yAxis.getLowerBound());
double minY = yAxis.getDisplayPosition(lowerY);
setStartY(getYPositionParent(minY, pane));
Number upperY = yAxis.toRealValue(yAxis.getUpperBound());
double maxY = yAxis.getDisplayPosition(upperY);
setEndY(getYPositionParent(maxY, pane));
double xPosInAxis = xAxis.getDisplayPosition(value);
setStartX(getXPositionParent(xPosInAxis, pane));
setEndX(getStartX());
pane.getChildren().add(this);
}
private double getXPositionParent(double xPosInAxis, Node parent){
double xPosInParent = xPosInAxis - xAxis.getBoundsInLocal().getMinX();
Node node = xAxis;
while(!node.equals(parent)){
xPosInParent = xPosInParent + node.getBoundsInParent().getMinX();
node = node.getParent();
}
return xPosInParent;
}
private double getYPositionParent(double yPosInAxis, Node parent){
double yPosInParent = yPosInAxis - yAxis.getBoundsInLocal().getMinY();
Node node = yAxis;
while(!node.equals(parent)){
yPosInParent = yPosInParent + node.getBoundsInParent().getMinY();
node = node.getParent();
}
return yPosInParent;
}
public void updateMarker(double value){
pane.getChildren().remove(this);
double xPosInAxis = xAxis.getDisplayPosition(value);
setStartX(getXPositionParent(xPosInAxis, pane));
setEndX(getStartX());
pane.getChildren().add(this);
pane.requestLayout();
}
Upvotes: 1
Reputation: 4208
It turned out that the solution is much easier than I thought.
Each JavaFX component (Node
) has a cache
property which can be set to true
in order to cache the rendered node as bitmap. This was exactly what I was looking for.
An efficient solution is therefore to put the chart and all marker lines into a StackPane
and enable caching for the chart. The rest is already explained in Adding a line in a JavaFX chart and How to add a value marker to JavaFX chart? (how to create the StackPane and how to calculate and update the marker lines).
Upvotes: 1