我使用了一个JavaFX LineChart,它需要几乎一秒钟才能渲染,我想在这个图表上移动一个垂直标记(光标位置),这个图表经常更新。
我测试了以下解决方案:
但是,这些解决方案不适用于大型图表上的频繁标记更新。只要标记发生变化,整个图表就会重新绘制。
如何在标记更新时阻止Java重绘整个图表? 我想到了一个包装器组件,只要图表内容和图表边界不变,就可以放在图表周围并缓存图表图像。这样的事情是存在的还是如何实现呢?
答案 0 :(得分:1)
事实证明,解决方案比我想象的容易得多。
每个JavaFX组件(Node
)都有一个cache
属性,可以将其设置为true
,以便将呈现的节点缓存为位图。这正是我想要的。
因此,有效的解决方案是将图表和所有标记行放入StackPane
并为图表启用缓存。其余部分已在Adding a line in a JavaFX chart和How 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();
}