问题
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 PathElement
s 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