java - 如何使用 JavaFX LineChart 添加两条垂直线

标签 java javafx

我的桌面应用程序有一个用于开始和停止测试的计时器。在图表上,我想创建两条垂直线来指示开始和停止时间。 “使用 JavaFX 向 StackPane 添加垂直线”不适用于我的情况,因为我不希望这些线保持在同一位置,并且这些线应该在绘图而不是布局中绘制。当用户在图表上缩放时,这些垂直线应相应于用户缩放的位置移动。感谢您的任何提示。

这是我创建图表的代码:

LineChart<Number, Number> chart = new LineChart<Number, Number>(xAxis, yAxis, dataset);

xAxis.setLabel("time(s)");
yAxis.setLabel("deg/s");

最佳答案

您需要扩展 LineChart 类并覆盖 layoutPlotChildren 方法以显示您的标记。

克利奥帕特拉做了一个 very good example for a Scatter chart .下面的代码是折线图的修改版本,同时具有垂直和水平标记:

public class LineChartSample extends Application {

    @Override public void start(Stage stage) {

        final NumberAxis xAxis = new NumberAxis();
        final NumberAxis yAxis = new NumberAxis();
        xAxis.setLabel("Number of Month");

        final LineChartWithMarkers<Number,Number> lineChart = new LineChartWithMarkers<Number,Number>(xAxis,yAxis);

        XYChart.Series series = new XYChart.Series();
        series.setName("My portfolio");

        series.getData().add(new XYChart.Data(1, 23));
        series.getData().add(new XYChart.Data(2, 14));
        series.getData().add(new XYChart.Data(3, 15));
        series.getData().add(new XYChart.Data(4, 24));
        series.getData().add(new XYChart.Data(5, 34));
        series.getData().add(new XYChart.Data(6, 36));
        series.getData().add(new XYChart.Data(7, 22));
        series.getData().add(new XYChart.Data(8, 45));
        series.getData().add(new XYChart.Data(9, 43));
        series.getData().add(new XYChart.Data(10, 17));
        series.getData().add(new XYChart.Data(11, 29));
        series.getData().add(new XYChart.Data(12, 25));

        lineChart.getData().add(series);

        Data<Number, Number> horizontalMarker = new Data<>(0, 25);
        lineChart.addHorizontalValueMarker(horizontalMarker);

        Data<Number, Number> verticalMarker = new Data<>(10, 0);
        lineChart.addVerticalValueMarker(verticalMarker);

        Slider horizontalMarkerSlider = new Slider(yAxis.getLowerBound(), yAxis.getUpperBound(), 0);
        horizontalMarkerSlider.setOrientation(Orientation.VERTICAL);
        horizontalMarkerSlider.setShowTickLabels(true);
        horizontalMarkerSlider.valueProperty().bindBidirectional(horizontalMarker.YValueProperty());
        horizontalMarkerSlider.minProperty().bind(yAxis.lowerBoundProperty());
        horizontalMarkerSlider.maxProperty().bind(yAxis.upperBoundProperty());

        Slider verticalMarkerSlider = new Slider(xAxis.getLowerBound(), xAxis.getUpperBound(), 0);
        verticalMarkerSlider.setOrientation(Orientation.HORIZONTAL);
        verticalMarkerSlider.setShowTickLabels(true);
        verticalMarkerSlider.valueProperty().bindBidirectional(verticalMarker.XValueProperty());
        verticalMarkerSlider.minProperty().bind(xAxis.lowerBoundProperty());
        verticalMarkerSlider.maxProperty().bind(xAxis.upperBoundProperty());

        BorderPane borderPane = new BorderPane();
        borderPane.setCenter( lineChart);
        borderPane.setTop(verticalMarkerSlider);
        borderPane.setRight(horizontalMarkerSlider);

        Scene scene  = new Scene(borderPane,800,600);

        stage.setScene(scene);
        stage.show();
    }

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

    private class LineChartWithMarkers<X,Y> extends LineChart {

        private ObservableList<Data<X, Y>> horizontalMarkers;
        private ObservableList<Data<X, Y>> verticalMarkers;

        public LineChartWithMarkers(Axis<X> xAxis, Axis<Y> yAxis) {
            super(xAxis, yAxis);
            horizontalMarkers = FXCollections.observableArrayList(data -> new Observable[] {data.YValueProperty()});
            horizontalMarkers.addListener((InvalidationListener)observable -> layoutPlotChildren());
            verticalMarkers = FXCollections.observableArrayList(data -> new Observable[] {data.XValueProperty()});
            verticalMarkers.addListener((InvalidationListener)observable -> layoutPlotChildren());
        }

        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);
        }

        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);
        }

        public void addVerticalValueMarker(Data<X, Y> marker) {
            Objects.requireNonNull(marker, "the marker must not be null");
            if (verticalMarkers.contains(marker)) return;
            Line line = new Line();
            marker.setNode(line );
            getPlotChildren().add(line);
            verticalMarkers.add(marker);
        }

        public void removeVerticalValueMarker(Data<X, Y> marker) {
            Objects.requireNonNull(marker, "the marker must not be null");
            if (marker.getNode() != null) {
                getPlotChildren().remove(marker.getNode());
                marker.setNode(null);
            }
            verticalMarkers.remove(marker);
        }


        @Override
        protected void layoutPlotChildren() {
            super.layoutPlotChildren();
            for (Data<X, Y> horizontalMarker : horizontalMarkers) {
                Line line = (Line) horizontalMarker.getNode();
                line.setStartX(0);
                line.setEndX(getBoundsInLocal().getWidth());
                line.setStartY(getYAxis().getDisplayPosition(horizontalMarker.getYValue()) + 0.5); // 0.5 for crispness
                line.setEndY(line.getStartY());
                line.toFront();
            }
            for (Data<X, Y> verticalMarker : verticalMarkers) {
                Line line = (Line) verticalMarker.getNode();
                line.setStartX(getXAxis().getDisplayPosition(verticalMarker.getXValue()) + 0.5);  // 0.5 for crispness
                line.setEndX(line.getStartX());
                line.setStartY(0d);
                line.setEndY(getBoundsInLocal().getHeight());
                line.toFront();
            }      
        }

    }
}

enter image description here

为了添加更多标记线,只需使用这个:

Data<Number, Number> verticalMarker = new Data<>(10, 0);
lineChart.addVerticalValueMarker(verticalMarker);

当然你也可以使用矩形而不是像这样的线:

private ObservableList<Data<X, X>> verticalRangeMarkers;

public LineChartWithMarkers(Axis<X> xAxis, Axis<Y> yAxis) {
    ...            
    verticalRangeMarkers = FXCollections.observableArrayList(data -> new Observable[] {data.XValueProperty()});
    verticalRangeMarkers = FXCollections.observableArrayList(data -> new Observable[] {data.YValueProperty()}); // 2nd type of the range is X type as well
    verticalRangeMarkers.addListener((InvalidationListener)observable -> layoutPlotChildren());
}        


public void addVerticalRangeMarker(Data<X, X> marker) {
    Objects.requireNonNull(marker, "the marker must not be null");
    if (verticalRangeMarkers.contains(marker)) return;

    Rectangle rectangle = new Rectangle(0,0,0,0);
    rectangle.setStroke(Color.TRANSPARENT);
    rectangle.setFill(Color.BLUE.deriveColor(1, 1, 1, 0.2));

    marker.setNode( rectangle);

    getPlotChildren().add(rectangle);
    verticalRangeMarkers.add(marker);
}

public void removeVerticalRangeMarker(Data<X, X> marker) {
    Objects.requireNonNull(marker, "the marker must not be null");
    if (marker.getNode() != null) {
        getPlotChildren().remove(marker.getNode());
        marker.setNode(null);
    }
    verticalRangeMarkers.remove(marker);
}

protected void layoutPlotChildren() {

    ...

    for (Data<X, X> verticalRangeMarker : verticalRangeMarkers) {

        Rectangle rectangle = (Rectangle) verticalRangeMarker.getNode();
        rectangle.setX( getXAxis().getDisplayPosition(verticalRangeMarker.getXValue()) + 0.5);  // 0.5 for crispness
        rectangle.setWidth( getXAxis().getDisplayPosition(verticalRangeMarker.getYValue()) - getXAxis().getDisplayPosition(verticalRangeMarker.getXValue()));
        rectangle.setY(0d);
        rectangle.setHeight(getBoundsInLocal().getHeight());
        rectangle.toBack();

    }
} 

这样使用:

Data<Number, Number> verticalRangeMarker = new Data<>(4, 10);
lineChart.addVerticalRangeMarker(verticalRangeMarker);

让它看起来像一个范围:

enter image description here

关于java - 如何使用 JavaFX LineChart 添加两条垂直线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28952133/

相关文章:

java - 当我从 native Java 方法抛出 C++ 异常时会发生什么?

java - 确定 LayoutManager 预布局中出现的 View

JavaFX 简单更新标签(线程)

java - 在 JavaFx 中使用 Css 编辑菜单栏

java - 是否可以获取 Class<SomeGenericClass<SomeType>> 的实例

java - JSF 中的 Ajax 表单更新显示 ViewExpiredException

java - AdapterView.OnItemClickListener 不起作用

JavaFX 使用 Smack API (xmpp) 从 Java 回调

java - 如何从 javaFx 中同一数据库中的两个 mysql 表填充 TableView ?

JavaFX ImageView : Can I retrieve the scaled image size when preserveRatio = true?