LineChart FX - Delete solid line

最后都变了- 提交于 2019-12-02 06:31:06

问题


I have a curious question about the graph LineChart JavaFX.

I have this graph: dots forming a "jump" on the X axis (as shown by the two red points I scored) and therefore JavaFX draws me the line between these two points. How do I remove that line between each "jump"?

I post the code:

public class ControllerIndividua {

    public static void plotIndividuaFull(String path, Stage stage, String name) {
        final NumberAxis xAxisIntensity = new NumberAxis(); //Dichiarazione asseX
        final NumberAxis yAxisIntensity = new NumberAxis();//Dichiarazione asseY

        DetectionS1.countS1();

        //Dichiarazione del tipo di grafico
        final LineChart<Number, Number> lineChartIntensity = new LineChart<Number, Number>(xAxisIntensity,yAxisIntensity);

        ArrayList<Double> extractedData; //Lista dei valori dello dell' intensità
        ArrayList<Double> extractedTime; //Lista del tempo
        ArrayList<Double> extractedS1; //Lista del tempo
        ArrayList<Double> extractedS1Time; //Lista del tempo

        //Gestione e settaggio del grafico
        lineChartIntensity.getData().clear();

        try {
            //Popolamento delle liste
            extractedTime = IntensityExtractor.pointsTime();
            extractedData = IntensityExtractor.pointsIntensity();
            extractedS1 = DetectionS1.S1pitch();
            extractedS1Time = DetectionS1.pointsS1Time();
            XYChart.Series<Number, Number> series = new XYChart.Series<Number, Number>();
            XYChart.Series<Number, Number> seriesS1 = new XYChart.Series<Number, Number>(); //Creazione seconda serie
            series.setName("Intensità di:\t" + name.toUpperCase());

            for (int j = 0; j < extractedS1.size(); j++) {
                seriesS1.getData().add(new XYChart.Data<Number, Number>(extractedS1Time.get(j), extractedS1.get(j)));
                lineChartIntensity.getStyleClass().add("CSSintensity");
            }

            //Creazione finestra e stampa del grafico
            Scene scene = new Scene(lineChartIntensity, 1000, 600);
            lineChartIntensity.getData().addAll(series,seriesS1);
            scene.getStylesheets().add("application/application.css");
            stage.setScene(scene);
            stage.show();
        } catch (java.lang.Exception e) {
            e.printStackTrace();
        }
    }
}

Someone also has a little idea on how I could do?

Thank you all in advance.


回答1:


This is an old question with an accepted answer but I came across it and was curious. I wanted to know if it was possible to put a gap in a LineChart (at least without having to create a custom chart implementation). It turns out that there is. The solution is kind of hacky and brittle. It involves getting the Path by using XYChart.Series.getNode() and manipulating the list of PathElements. The following code gives an example:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.LineTo;
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) {
        LineChart<Number, Number> chart = new LineChart<>(new NumberAxis(), new NumberAxis());
        chart.getXAxis().setLabel("X");
        chart.getYAxis().setLabel("Y");
        chart.setLegendVisible(false);
        chart.getData().add(new XYChart.Series<>());

        for (int x = 0; x <= 10; x++) {
            chart.getData().get(0).getData().add(new XYChart.Data<>(x, Math.pow(x, 2)));
        }

        /*
         * Had to wrap the call in a Platform.runLater otherwise the Path was
         * redrawn after the modifications are made.
         */
        primaryStage.setOnShown(we -> Platform.runLater(() -> {
            Path path = (Path) chart.getData().get(0).getNode();
            LineTo lineTo = (LineTo) path.getElements().get(8);
            path.getElements().set(8, new MoveTo(lineTo.getX(), lineTo.getY()));
        }));

        primaryStage.setScene(new Scene(new StackPane(chart), 500, 300));
        primaryStage.setTitle("LineChart Gap");
        primaryStage.show();
    }

}

This code results in the following:

This is possible because the ObservableList of PathElements seems to be a MoveTo followed by a bunch of LineTos. I simply picked a LineTo and replaced it with a MoveTo to the same coordinates. I haven't quite figured out which index of LineTo matches with which XYChart.Data, however, and picked 8 for the example randomly.

There are a couple of issues with this solution. The first and obvious one is that this relies on the internal implementation of LineChart. The second is where the real brittleness comes from though. Any change to the data, either axes' value ranges, the chart's width or height, or pretty much anything that causes the chart to redraw itself will cause the Path to be recomputed and redrawn. This means if you use this solution you'll have to reapply the modification every time the chart redraws itself.




回答2:


I made a GapLineChart that automatically sets a MoveTo like Slaw pointed it whenever it sees a Double.NaN. You can find it here https://gist.github.com/sirolf2009/ae8a7897b57dcf902b4ed747b05641f9. Check the first comment for an example




回答3:


I checked it. It is not possible to change only one part of this line. Because this is one long Path and you can't change one element of Path.

I think the only way is add each "jump" in different series.




回答4:


I was in need of adapting LineChart so that it drew line segments, so each pair of data points created a single line segment. It's pretty straight-forward to do by extending LineChart and overriding the layoutPlotChildren function, although I had to delete an animation y-scaling variable as it was private in the super class, but I'm not animating the graph so it was fine. Anyway, here is the Kotlin version (which you can easily adapt to Java, or just copy the Java LineChart layoutPlotChildren code and adapt). It's easy to adapt to your circumstances, just change the contents of the for loop at the very end.

import javafx.scene.chart.Axis
import javafx.scene.chart.LineChart
import javafx.scene.shape.LineTo
import javafx.scene.shape.MoveTo
import javafx.scene.shape.Path
import java.util.*
import kotlin.collections.ArrayList

class GapLineChart<X, Y>(xAxis: Axis<X>?, yAxis: Axis<Y>?) : LineChart<X, Y>(xAxis, yAxis) {

    public override fun layoutPlotChildren() {

        val constructedPath = ArrayList<LineTo>(data.size)

        for (seriesIndex in 0 until data.size) {

            val series = data[seriesIndex]

            if (series.node is Path) {

                val seriesLine = (series.node as Path).elements
                val it = getDisplayedDataIterator(series)

                seriesLine.clear()
                constructedPath.clear()

                var parity = true

                while (it.hasNext()) {

                    val item = it.next()
                    val x = xAxis.getDisplayPosition(item.xValue)
                    val y = yAxis.getDisplayPosition(yAxis.toRealValue(yAxis.toNumericValue(item.yValue)))

                    if (java.lang.Double.isNaN(x) || java.lang.Double.isNaN(y)) {
                        continue
                    }

                    if(parity)
                        constructedPath.add(LineTo(x, y))

                    val symbol = item.node

                    if (symbol != null) {

                        val w = symbol.prefWidth(-1.0)
                        val h = symbol.prefHeight(-1.0)

                        symbol.resizeRelocate(x - w / 2, y - h / 2, w, h)
                    }
                }
                when (axisSortingPolicy) {

                    SortingPolicy.X_AXIS -> constructedPath.sortWith(Comparator { e1, e2 -> java.lang.Double.compare(e1.x, e2.x) })
                    SortingPolicy.Y_AXIS -> constructedPath.sortWith(Comparator { e1, e2 -> java.lang.Double.compare(e1.y, e2.y) })
                }

                if (!constructedPath.isEmpty()) {

                    for (i in 0 until constructedPath.size-1 step 2) {

                        seriesLine.add(MoveTo(constructedPath[i].x, constructedPath[i].y))
                        seriesLine.add(LineTo(constructedPath[i+1].x, constructedPath[i+1].y))
                    }
                }
            }
        }
    }
}


来源:https://stackoverflow.com/questions/42424166/linechart-fx-delete-solid-line

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!