高效地更新JavaFX图表顶部的线条/标记

高效地更新JavaFX图表顶部的线条/标记,java,javafx-2,javafx-8,Java,Javafx 2,Javafx 8,我使用一个JavaFX折线图,它需要将近一秒钟的时间来渲染,我想在这个经常更新的图表上移动一个垂直标记(光标位置) 我测试了以下解决方案: 然而,这些解决方案并不适用于大型图表上频繁的标记更新。每当标记改变时,整个图表都会重新绘制 如何防止Java在标记更新时重新绘制整个图表? 我想到了一个包装器组件,只要图表内容和图表边界不变,它就可以放在图表周围并缓存图表图像。类似的东西是否存在,或者如何实现这一点?事实证明,解决方案比我想象的要简单得多。 每个JavaFX组件(节点)都有一个缓存属

我使用一个JavaFX折线图,它需要将近一秒钟的时间来渲染,我想在这个经常更新的图表上移动一个垂直标记(光标位置)

我测试了以下解决方案:

然而,这些解决方案并不适用于大型图表上频繁的标记更新。每当标记改变时,整个图表都会重新绘制

如何防止Java在标记更新时重新绘制整个图表?
我想到了一个包装器组件,只要图表内容和图表边界不变,它就可以放在图表周围并缓存图表图像。类似的东西是否存在,或者如何实现这一点?

事实证明,解决方案比我想象的要简单得多。 每个JavaFX组件(
节点
)都有一个
缓存
属性,可以将该属性设置为
true
,以便将渲染节点缓存为位图。这正是我要找的


因此,一个有效的解决方案是将图表和所有标记行放入
堆栈窗格
,并为图表启用缓存。其余部分已在和(如何创建StackPane以及如何计算和更新标记线)中进行了说明。

正如Stefan所述,最好的方法是将不想更新的节点的
缓存属性设置为
true
。我想补充的是,
StackPane
并不是绝对必要的,它可以是一个
Pane
或另一个区域(实际上,StackPane有自己的方式来添加新的子项,这使您难以操作)。 下面是我用来实现沿着
折线图移动的线标记的代码片段(在我的示例中,我有一个视频,线标记跟随图表中的时间)

主要例子

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()));
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();
}