Unfortunately, ValueMarkers are not supported in XYCharts (which, probably, a place in the hierarchy should have been made) (Mis-), using data with a constant value, this is a hack that may (or may not) be acceptable / possible in some contexts.
A cleaner output is a custom chart that supports such markers. FI custom ScatterChart, for example:
public class ScatterXChart<X, Y> extends ScatterChart<X, Y> { // data defining horizontal markers, xValues are ignored private ObservableList<Data<X, Y>> horizontalMarkers; public ScatterXChart(Axis<X> xAxis, Axis<Y> 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 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<X, Y> 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<X, Y> 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<X, Y> 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()); } } }
A fragment of testing a chart (fi insert into an online example in the oracle textbook):
// instantiate chart NumberAxis xAxis = new NumberAxis(0, 10, 1); NumberAxis yAxis = new NumberAxis(-100, 500, 100); ScatterXChart<Number,Number> sc = new ScatterXChart<>(xAxis,yAxis); // .. fill with some data ... // ui to add/change/remove a value marker Data<Number, Number> 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));
Application:
Although I would not recommend an approach in a related question (adding a marker line to the parent of the chart and controlling its position / length from the outside), you can use it in resizable containers. The most important things to make it work:
- listen to changes in size / location of the chart and update the line accordingly.
- set marker driven property to false
in code (updateShift is the part of the original that calculates 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);