且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何向 JavaFX 图表添加值标记?

更新时间:2023-12-05 21:28:28

要将图表值转换为像素,您可以使用方法 NumberAxis#getDisplayPosition() 返回图表节点的实际坐标.>

虽然这些坐标是相对于图表区域的,您可以通过下一个代码找到:

Node chartArea = chart.lookup(".chart-plot-background");边界 chartAreaBounds = chartArea.localToScene(chartArea.getBoundsInLocal());

注意 localToScene() 方法,它允许您将任何坐标转换为场景坐标.因此,您可以使用它们来更新您的值标记坐标.确保在显示场景后调用 localToScene.

请参阅下面的示例程序,该程序生成下一个图表:

public class LineChartValueMarker extends Application {私人线值标记 = 新线();私人 XYChart.Series系列 = 新的 XYChart.Series<>();私有 NumberAxis yAxis;私人双yShift;私有无效更新标记(){//找到最大的 y 值双最大 = 0;for (Data value : series.getData()) {double y = value.getYValue().doubleValue();如果 (y > 最大值) {最大值 = y;}}//找到该值的像素位置double displayPosition = yAxis.getDisplayPosition(max);//更新标记valueMarker.setStartY(yShift + displayPosition);valueMarker.setEndY(yShift + displayPosition);}@覆盖公共无效开始(阶段阶段){LineChartchart = new LineChart<>(new NumberAxis(0, 100, 10), yAxis = new NumberAxis(0, 100, 10));series.getData().add(new XYChart.Data(0, 0));series.getData().add(new XYChart.Data(10, 20));chart.getData().addAll(series);窗格窗格 = new Pane();pane.getChildren().addAll(chart, valueMarker);场景场景 = 新场景(窗格);//在鼠标单击时添加新值以进行测试chart.setOnMouseClicked(new EventHandler() {@覆盖公共无效句柄(鼠标事件 t){series.getData().add(new XYChart.Data(series.getData().size() * 10, 30 + 50 * new Random().nextDouble()));更新标记();}});stage.setScene(场景);舞台表演();//找到图表区域节点节点 chartArea = chart.lookup(".chart-plot-background");边界 chartAreaBounds = chartArea.localToScene(chartArea.getBoundsInLocal());//记住图表区域的场景位置yShift = chartAreaBounds.getMinY();//将 valueMarker 的 x 参数设置为图表区域边界valueMarker.setStartX(chartAreaBounds.getMinX());valueMarker.setEndX(chartAreaBounds.getMaxX());更新标记();}公共静态无效主(字符串 [] args){发射();}}

I am trying to build a series chart using JavaFX, where data is inserted dynamically.

Each time that a new value is inserted I would like to check if this is the highest value so far, and if so, I want to draw an horizontal line to show that this is the maximum value.

In JFree chart I would have used a ValueMarker, but I am trying to do the same with JavaFX.

I tried using the Line object, but it is definitely not the same, because I cannot provide the Chart values, it takes the relative pixel positions in the windows.

Here is the screenshot of chart I want to achieve:

http://postimg.org/image/s5fkupsuz/

Any suggestions? Thank you.

To convert chart values to pixels you can use method NumberAxis#getDisplayPosition() which return actual coordinates of the chart nodes.

Although these coordinates are relative to chart area, which you can find out by next code:

Node chartArea = chart.lookup(".chart-plot-background");
Bounds chartAreaBounds = chartArea.localToScene(chartArea.getBoundsInLocal());

Note localToScene() method which allows you to convert any coordinates to Scene ones. Thus you can use them to update your value marker coordinates. Make sure you make localToScene call after your Scene have been shown.

See sample program below which produces next chart:

public class LineChartValueMarker extends Application {

    private Line valueMarker = new Line();
    private XYChart.Series<Number, Number> series = new XYChart.Series<>();
    private NumberAxis yAxis;
    private double yShift;

    private void updateMarker() {
        // find maximal y value
        double max = 0;
        for (Data<Number, Number> value : series.getData()) {
            double y = value.getYValue().doubleValue();
            if (y > max) {
                max = y;
            }
        }
        // find pixel position of that value
        double displayPosition = yAxis.getDisplayPosition(max);
        // update marker
        valueMarker.setStartY(yShift + displayPosition);
        valueMarker.setEndY(yShift + displayPosition);
    }

    @Override
    public void start(Stage stage) {
        LineChart<Number, Number> chart = new LineChart<>(new NumberAxis(0, 100, 10), yAxis = new NumberAxis(0, 100, 10));

        series.getData().add(new XYChart.Data(0, 0));
        series.getData().add(new XYChart.Data(10, 20));

        chart.getData().addAll(series);
        Pane pane = new Pane();
        pane.getChildren().addAll(chart, valueMarker);
        Scene scene = new Scene(pane);

        // add new value on mouseclick for testing
        chart.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent t) {
                series.getData().add(new XYChart.Data(series.getData().size() * 10, 30 + 50 * new Random().nextDouble()));
                updateMarker();
            }
        });

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

        // find chart area Node
        Node chartArea = chart.lookup(".chart-plot-background");
        Bounds chartAreaBounds = chartArea.localToScene(chartArea.getBoundsInLocal());
        // remember scene position of chart area
        yShift = chartAreaBounds.getMinY();
        // set x parameters of the valueMarker to chart area bounds
        valueMarker.setStartX(chartAreaBounds.getMinX());
        valueMarker.setEndX(chartAreaBounds.getMaxX());
        updateMarker();
    }

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