Aggiunta di una linea in un grafico JavaFX

Ho problemi con l’aggiunta di una linea in una posizione definita in JavaFX. La linea deve essere allineata a una linea costante come mostrato qui: Come aggiungere un indicatore di valore alla tabella JavaFX?

Il mio problema è che la mia definizione del layout è un po ‘più complicata. Dare un’occhiata:

grafico

La parte importante è quella in alto. Voglio avere la linea sulla linea y = 60. La parte sinistra con i RadioBox è una VBox. La parte con (Scatter-) Chart è uno StackPane (perché voglio riempire il resto della larghezza). All’interno di questo StackPane c’è il grafico e un gruppo. L’unico figlio del gruppo è la linea.

Penso che il problema sia che lo StackPane centra il Gruppo con la linea sopra il grafico. Ma non riesco a ottenere la combinazione di layout che 1. allunga il grafico 2. imposta la linea sopra il grafico 3. non centra la linea

Ho provato un sacco di combinazioni ma non riesco a ottenerlo nel modo che voglio. Qualcuno ha un’idea?!

Sfortunatamente, ValueMarkers non è supportato in XYCharts (probabilmente è il posto nella gerarchia dove dovrebbe essere fatto) (Mis-) l’uso di dati con valore costante è un hack che potrebbe (o non) essere accettabile / ansible in alcuni contesti.

Una via d’uscita più pulita è una tabella personalizzata che supporta tali indicatori. Fi uno ScatterChart personalizzato come:

public class ScatterXChart extends ScatterChart { // data defining horizontal markers, xValues are ignored private ObservableList> horizontalMarkers; public ScatterXChart(Axis xAxis, Axis yAxis) { super(xAxis, yAxis); // a list that notifies on change of the yValue property horizontalMarkers = FXCollections.observableArrayList(d -> new Observable[] {d.YValueProperty()}); // listen to list changes and re-plot horizontalMarkers.addListener((InvalidationListener)observable -> layoutPlotChildren()); } /** * Add horizontal value marker. The marker's Y value is used to plot a * horizontal line across the plot area, its X value is ignored. * * @param marker must not be null. */ public void addHorizontalValueMarker(Data marker) { Objects.requireNonNull(marker, "the marker must not be null"); if (horizontalMarkers.contains(marker)) return; Line line = new Line(); marker.setNode(line ); getPlotChildren().add(line); horizontalMarkers.add(marker); } /** * Remove horizontal value marker. * * @param horizontalMarker must not be null */ public void removeHorizontalValueMarker(Data marker) { Objects.requireNonNull(marker, "the marker must not be null"); if (marker.getNode() != null) { getPlotChildren().remove(marker.getNode()); marker.setNode(null); } horizontalMarkers.remove(marker); } /** * Overridden to layout the value markers. */ @Override protected void layoutPlotChildren() { super.layoutPlotChildren(); for (Data horizontalMarker : horizontalMarkers) { double lower = ((ValueAxis) getXAxis()).getLowerBound(); X lowerX = getXAxis().toRealValue(lower); double upper = ((ValueAxis) getXAxis()).getUpperBound(); X upperX = getXAxis().toRealValue(upper); Line line = (Line) horizontalMarker.getNode(); line.setStartX(getXAxis().getDisplayPosition(lowerX)); line.setEndX(getXAxis().getDisplayPosition(upperX)); line.setStartY(getYAxis().getDisplayPosition(horizontalMarker.getYValue())); line.setEndY(line.getStartY()); } } } 

Un frammento di prova del grafico (inseriscilo nell’esempio online nel tutorial di oracle):

 // instantiate chart NumberAxis xAxis = new NumberAxis(0, 10, 1); NumberAxis yAxis = new NumberAxis(-100, 500, 100); ScatterXChart sc = new ScatterXChart<>(xAxis,yAxis); // .. fill with some data ... // ui to add/change/remove a value marker Data horizontalMarker = new Data<>(0, 110); Button add = new Button("Add Marker"); add.setOnAction(e -> sc.addHorizontalValueMarker(horizontalMarker)); Slider move = new Slider(yAxis.getLowerBound(), yAxis.getUpperBound(), 0); move.setShowTickLabels(true); move.valueProperty().bindBidirectional(horizontalMarker.YValueProperty()); Button remove = new Button("Remove Marker"); remove.setOnAction(e -> sc.removeHorizontalValueMarker(horizontalMarker)); 

Addendum:

Mentre non consiglierei l’ approccio nella domanda correlata (aggiungendo la linea del marker al genitore del grafico e gestendone la posizione / la lunghezza esternamente) è ansible utilizzarlo in contenitori ridimensionabili. Le cose fondamentali per farlo funzionare:

  • ascoltare le dimensioni del diagramma / le modifiche alla posizione e aggiornare la linea in modo appropriato
  • imposta la proprietà gestita del marcatore su falso

nel codice (updateShift è la parte nell’originale che calcola il yShift / lineX):

 Pane pane = new StackPane(chart); chart.widthProperty().addListener(o -> updateShift(chart)); chart.heightProperty().addListener(o -> updateShift(chart)); valueMarker.setManaged(false); pane.getChildren().add(valueMarker); 

Perché non aggiungi questa linea al tuo grafico? Ho grafici in cui visualizzo i 100, 99, 95 e 50 percentili e il modo in cui risolvo questo è di aggiungere una linea al valore y corretto per ogni percentile.

Per fare ciò basta aggiungere una linea con due punti, uno a y = 60 x = 70 (il valore dell’asse x più a sinistra) e l’altro a y = 60 e x = 120 (l’asse x più a destra valore).

Il lato positivo di questo è che non è necessario allineare manualmente la linea orizzontale, il lato negativo è che questa linea orizzontale farà anche parte della legenda. Tuttavia, visto che non hai una legenda che dovrebbe essere OK.

Se decidi di aggiungere una legenda, assicurati di nominare la linea orizzontale in modo appropriato, l