有效地更新JavaFX图表上的行/标记

时间:2015-05-27 12:14:58

标签: java javafx-2 javafx-8

我使用了一个JavaFX LineChart,它需要几乎一秒钟才能渲染,我想在这个图表上移动一个垂直标记(光标位置),这个图表经常更新。

我测试了以下解决方案:

但是,这些解决方案不适用于大型图表上的频繁标记更新。只要标记发生变化,整个图表就会重新绘制。

如何在标记更新时阻止Java重绘整个图表? 我想到了一个包装器组件,只要图表内容和图表边界不变,就可以放在图表周围并缓存图表图像。这样的事情是存在的还是如何实现呢?

2 个答案:

答案 0 :(得分:1)

事实证明,解决方案比我想象的容易得多。 每个JavaFX组件(Node)都有一个cache属性,可以将其设置为true,以便将呈现的节点缓存为位图。这正是我想要的。

因此,有效的解决方案是将图表和所有标记行放入StackPane并为图表启用缓存。其余部分已在Adding a line in a JavaFX chartHow to add a value marker to JavaFX chart?中解释(如何创建StackPane以及如何计算和更新标记线)。

答案 1 :(得分:0)

正如Stefan所提到的,最好的方法是将cache属性设置为您不想更新的节点的true。我要补充的是StackPane并不是非常必要的,它可能是Pane或其他区域(实际上StackPane有自己的方式来添加新的子项,这会让你难以操作)。 下面是我用来实现沿LineChart移动的LineMarker的代码片段(在我的示例中,我有一个视频,LineMarker跟随图表中的时间)

主要例子

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