有没有办法在JavaFX LineChart中断开2个串联串?

时间:2016-07-26 13:37:39

标签: java javafx linechart

我在LineChart上有四个系列。每个系列都包含一些时间分割的图形。默认情况下,LineChart连接这些图形。它看起来很丑,在上下文中没有任何意义,所以我想将它们分开,但保留颜色和图例。换句话说,我想要的是删除两个特定点之间的连接。有没有办法在不借助向图表中添加新系列的情况下执行此操作(图形在逻辑上是连接的,添加新系列会使用户混淆并使图表混乱)?

for(int j = 0; j < 4; j++) {
    XYChart.Series<Float, Float> series = new XYChart.Series<>();
    series.setName("Канал " + (j + 1));
    fillWithData(series);
    chart.getData().add(series);
}

enter image description here

2 个答案:

答案 0 :(得分:2)

是的,你可以

https://docs.oracle.com/javase/8/javafx/api/javafx/scene/chart/XYChart.Series.html#nodeProperty中所述,系列节点用于存储线路。节点是您可以修改的路径。

看看这个小应用程序:

    package application;

import java.util.Arrays;
import java.util.List;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.shape.Circle;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {

            NumberAxis xAxis = new NumberAxis();
            NumberAxis yAxis = new NumberAxis();
            MyLineChart chart = new MyLineChart(xAxis, yAxis, Arrays.asList(5));


            XYChart.Series<Number, Number> serie = new Series<>();

            serie.getData().add(new Data<Number, Number>(0, 5));
            serie.getData().add(new Data<Number, Number>(1, 5));
            serie.getData().add(new Data<Number, Number>(2, 5));
            serie.getData().add(new Data<Number, Number>(3, 5));
            serie.getData().add(new Data<Number, Number>(4, 5));
            serie.getData().add(new Data<Number, Number>(5, -5));
            serie.getData().add(new Data<Number, Number>(6, -5));
            serie.getData().add(new Data<Number, Number>(7, -5));

            chart.getData().add(serie);

            Path p = (Path) serie.getNode();        

            Scene scene = new Scene(chart,400,400);
            primaryStage.setScene(scene);
            primaryStage.show();



    }

    public static void main(String[] args) {
        launch(args);
    }

    private static class MyLineChart extends LineChart<Number,Number>{


        private List<Integer> breakpointIndex;

        public MyLineChart(Axis<Number> xAxis, Axis<Number> yAxis, List<Integer> breakpointIndex) {
            super(xAxis, yAxis);
            this.breakpointIndex = breakpointIndex;
        }

        @Override
        protected void layoutPlotChildren() {
            super.layoutPlotChildren();

            Path p = (Path) getData().get(0).getNode();

            breakpointIndex.forEach(i ->{

                Data<Number, Number> discontinuousPoint = getData().get(0).getData().get(i+1);
                p.getElements().add(i+1, new MoveTo(getXAxis().getDisplayPosition( discontinuousPoint.getXValue()), getYAxis().getDisplayPosition(discontinuousPoint.getYValue())));

            });

            System.out.println("\nnew Path :");
            p.getElements().forEach(e -> System.out.println("p : " + e));


            //getPlotChildren().add(new Circle(50));


        }

    }
}

你必须覆盖负责绘制图表的方法layoutplotchildren(如果可以的话,看看原始方法的代码很有意思。)

然后你可以在这里画出你想要的东西。

在我的小应用程序中,我只是修改已经创建的节点,但你可以省略超级调用并绘制你想要的内容。

查看doc有访问chartelement的方法,比如getPlotChildren(),getchartchildren()。

您可以使用getdata()

访问添加到图表中的系列

答案 1 :(得分:0)

显然,在JavaFX 11中,用于填充Elements中基础Path列表的内部逻辑已更改。现在,如果我缩放图表(换句话说,更改轴的上下边界),它将仅填充可见元素,因此DataPathElement的索引不再匹配(并且各个列表的大小也没有)。我想这是一种优化。

我被迫用以下骇人听闻的方式重写代码:

@Override
protected void layoutPlotChildren() {
    super.layoutPlotChildren();
    for(Series<Number, Number> data : getData()) {
        ObservableList<PathElement> elements = ((Path)data.getNode()).getElements();
        for(int i = 0; i < elements.size() - 1; i++) {
            PathElement oldElement = elements.get(i);
            PathElement newElement = elements.get(i + 1);
            if (oldElement instanceof LineTo && newElement instanceof LineTo) {
                double oldDisplayX = ((LineTo) oldElement).getX();
                double newDisplayX = ((LineTo) newElement).getX();
                if(getXAxis().getValueForDisplay(newDisplayX).doubleValue() -
                       getXAxis().getValueForDisplay(oldDisplayX).doubleValue() > 10)
                    elements.set(i, new MoveTo(newDisplayX, ((LineTo) newElement).getY()));
            }
        }
    }
}

现在(对我而言)无需保留单独的断点索引列表,也无需手动添加它们,因为我仅在一种情况下插入了断点:X值之间的间隔大于10。我忽略了Data列表将完全遍历Elements列表,并在需要时将显示坐标转换为X值。对于大量元素,这显然会增加一些开销,因为每次窗口大小更改时或添加新元素时都会调用此方法,但是对于我的用例,此图由4系列6000-15000点组成,并且此大小计算大约需要1ms,这可以忽略不计。我也可以删除其中一个getValueForDisplay调用,不确定该如何先计算像素差。