java - 如何修复折线图十字准线的位置

标签 java javafx charts line linechart

因此,我成功实现了您的代码并将其更改为调用类 Cursor。这是光标的代码以及我如何在主 Controller 中调用它。因此,我决定制作一个光标作为我的图表的参数。当我按下一个按钮时,它会创建一个光标。但这不是我认为最好的方法,我想调用一个函数 createCursor 来向图表添加线条,但不可能向图表的子元素添加线条

public class Curseur<X, Y>  {

private Line vLine ;
private Line hLine ;
private boolean estUtilisé = false;


public Curseur(Line vLine, Line hLine, boolean estUtilise) {

    this.estUtilisé = estUtilisé;
    this.vLine = vLine;
    this.hLine = hLine;

}



public boolean isEstUtilisé() {
    return estUtilisé;
}

  public void moveCrossHair(double x, double y) {
      vLine.setStartX(x);
      hLine.setStartY(y);
  }



public void setEstUtilisé(boolean estUtilisé) {
    this.estUtilisé = estUtilisé;
}



public Line getvLine() {
    return vLine;
}

public void setvLine(Line vLine) {
    this.vLine = vLine;
}

public Line gethLine() {
    return hLine;
}

public void sethLine(Line hLine) {
    this.hLine = hLine;
}



    Line vLine = new Line();
    Line hLine = new Line();
    boolean used = false;
    Curseur curseur = new Curseur(vLine, hLine, used);

    final CrossHairLineChart<Number, Number> lineChart = 




   new CrossHairLineChart<Number, Number>(xAxis, yAxis, curseur);

    XYChart.Series<Number,Number> series1 = new XYChart.Series();
    series1.setName("Super");
    updateSeriesData(series1);

    XYChart.Series series2 = new XYChart.Series();
    series2.setName("Rate");
    updateSeriesData(series2);

    XYChart.Series series3 = new XYChart.Series();
    series3.setName("Stock");
    updateSeriesData(series3);

    lineChart.getData().addAll(series1, series2, series3);
    lineChart.prefHeightProperty().bind(chartTemplate.heightProperty());
    lineChart.prefWidthProperty().bind(chartTemplate.widthProperty());
    chartTemplate.getChildren().add(lineChart);

最佳答案

我个人认为十字准线应该在绘图区域的范围内。我可以看到线条超过了标签和图例。因此尝试在内部处理这个功能,以便可以重用。

下面是我的方法的快速演示。这个想法是创建一个自定义折线图并将线条添加到绘图区域并根据鼠标移动移动它们。

 import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.BorderPane;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

import java.security.SecureRandom;

public class LineChart_Crosshair_Demo extends Application {

    SecureRandom rnd = new SecureRandom();

    @Override
    public void start(Stage stage) {
        final NumberAxis xAxis = new NumberAxis();
        final NumberAxis yAxis = new NumberAxis();
        xAxis.setLabel("Number of Month");
        yAxis.setLabel("Data");

        XYChart.Series series1 = new XYChart.Series();
        series1.setName("Super");
        updateSeriesData(series1);

        XYChart.Series series2 = new XYChart.Series();
        series2.setName("Rate");
        updateSeriesData(series2);

        XYChart.Series series3 = new XYChart.Series();
        series3.setName("Stock");
        updateSeriesData(series3);

        Line vLine = new Line();
        Line hLine = new Line();
        Curseur curseur = new Curseur(vLine, hLine, true);

        final CrossHairLineChart<Number, Number> lineChart = new CrossHairLineChart<>(xAxis, yAxis, curseur);
        lineChart.getData().addAll(series1, series2, series3);

        CheckBox showCrosshairCB = new CheckBox("Show Crosshair");
        showCrosshairCB.setSelected(curseur.isEstUtilisé());
        showCrosshairCB.selectedProperty().addListener((obs, old, show) -> curseur.setEstUtilisé(show));

        BorderPane borderPane = new BorderPane();
        borderPane.setTop(showCrosshairCB);
        borderPane.setCenter(lineChart);
        borderPane.setPadding(new Insets(5));
        Scene scene = new Scene(borderPane, 800, 600);
        stage.setScene(scene);
        stage.show();
    }

    private void updateSeriesData(XYChart.Series series) {
        for (int i = 1; i < 13; i = (i + 2)) {
            series.getData().add(new XYChart.Data(i, rnd.nextInt(50)));
        }
    }

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

    private class Curseur {
        private Line vLine;
        private Line hLine;
        private BooleanProperty estUtilisé = new SimpleBooleanProperty();

        public Curseur(Line vLine, Line hLine, boolean estUtilisé) {
            this.vLine = vLine;
            this.hLine = hLine;
            this.estUtilisé.set(estUtilisé);
        }

        public Line getvLine() {
            return vLine;
        }

        public Line gethLine() {
            return hLine;
        }

        public boolean isEstUtilisé() {
            return estUtilisé.get();
        }

        public void setEstUtilisé(boolean estUtilisé) {
            this.estUtilisé.set(estUtilisé);
        }

        public BooleanProperty estUtiliséProperty() {
            return estUtilisé;
        }
    }

    /**
     * Custom line chart to include cross hair on plot area.
     *
     * @param <X>
     * @param <Y>
     */
    private class CrossHairLineChart<X, Y> extends LineChart {

        private Line vLine;
        private Line hLine;
        private Group plotArea;
        private BooleanProperty showFlag = new SimpleBooleanProperty();
        private BooleanProperty showCrossHair = new SimpleBooleanProperty();
        private double tickSize = 5;

        public CrossHairLineChart(Axis<X> xAxis, Axis<Y> yAxis, Curseur curseur) {
            super(xAxis, yAxis);
            vLine = curseur.getvLine();
            hLine = curseur.gethLine();
            showCrossHair.set(curseur.isEstUtilisé());
            curseur.estUtiliséProperty().addListener((obs, old, show) -> showCrossHair.set(show));

            hLine.endYProperty().bind(hLine.startYProperty());
            vLine.endXProperty().bind(vLine.startXProperty());
            vLine.visibleProperty().bind(showFlag);
            hLine.visibleProperty().bind(showFlag);
            setOnMouseExited(e -> showFlag.set(false));
            setOnMouseMoved(e -> {
                if (isShowCrossHair() && plotArea != null) {
                    Bounds b = plotArea.getBoundsInLocal();
                    // If the mouse cursor is within the plot area bounds
                    if (b.getMinX() < e.getX() && e.getX() < b.getMaxX() && b.getMinY() < e.getY() && e.getY() < b.getMaxY()) {
                        showFlag.set(true);
                        moveCrossHair(e.getX() - b.getMinX() - tickSize, e.getY() - b.getMinY() - tickSize);
                    } else {
                        showFlag.set(false);
                    }
                }
            });
        }

        private void moveCrossHair(double x, double y) {
            vLine.setStartX(x);
            hLine.setStartY(y);
        }

        public boolean isShowCrossHair() {
            return showCrossHair.get();
        }

        public BooleanProperty showCrossHairProperty() {
            return showCrossHair;
        }

        public void setShowCrossHair(boolean showCrossHair) {
            this.showCrossHair.set(showCrossHair);
        }

        @Override
        protected void layoutPlotChildren() {
            super.layoutPlotChildren();
            if (plotArea == null && !getPlotChildren().isEmpty()) {
                Group plotContent = (Group) ((Node) getPlotChildren().get(0)).getParent();
                plotArea = (Group) plotContent.getParent();
            }
            if (!getPlotChildren().contains(vLine)) {
                getPlotChildren().addAll(vLine, hLine);
            }
            hLine.setStartX(0);
            hLine.setEndX(getBoundsInLocal().getWidth());

            vLine.setStartY(0);
            vLine.setEndY(getBoundsInLocal().getHeight());
        }
    }
}

enter image description here

更新2[多个图表和卡住]

import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.CheckBox;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

import java.security.SecureRandom;

public class MultipleLineChart_Crosshair_Demo extends Application {

    SecureRandom rnd = new SecureRandom();

    @Override
    public void start(Stage stage) {

        Curseur curseur = new Curseur(-1, -1, true);

        CheckBox showCrosshairCB = new CheckBox("Show Crosshair");
        showCrosshairCB.setSelected(curseur.isEstUtilisé());
        showCrosshairCB.selectedProperty().addListener((obs, old, show) -> curseur.setEstUtilisé(show));

        CrossHairLineChart<Number, Number> lineChart1 = buildChart();
        lineChart1.setCursor(curseur);

        CrossHairLineChart<Number, Number> lineChart2 = buildChart();
        lineChart2.setCursor(curseur);

        BorderPane borderPane = new BorderPane();
        borderPane.setTop(showCrosshairCB);
        borderPane.setCenter(new VBox(lineChart1, lineChart2));
        borderPane.setPadding(new Insets(5));
        Scene scene = new Scene(borderPane, 800, 600);
        stage.setScene(scene);
        stage.show();
    }

    private CrossHairLineChart<Number, Number> buildChart() {
        final NumberAxis xAxis = new NumberAxis();
        final NumberAxis yAxis = new NumberAxis();
        xAxis.setLabel("Number of Month");
        yAxis.setLabel("Data");

        XYChart.Series series1 = new XYChart.Series();
        series1.setName("Super");
        updateSeriesData(series1);

        XYChart.Series series2 = new XYChart.Series();
        series2.setName("Rate");
        updateSeriesData(series2);

        XYChart.Series series3 = new XYChart.Series();
        series3.setName("Stock");
        updateSeriesData(series3);

        final CrossHairLineChart<Number, Number> lineChart = new CrossHairLineChart<>(xAxis, yAxis);
        lineChart.getData().addAll(series1, series2, series3);
        return lineChart;
    }

    private void updateSeriesData(XYChart.Series series) {
        for (int i = 1; i < 13; i = (i + 2)) {
            series.getData().add(new XYChart.Data(i, rnd.nextInt(50)));
        }
    }

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

    private class Curseur {
        private DoubleProperty x = new SimpleDoubleProperty();
        private DoubleProperty y = new SimpleDoubleProperty();
        private BooleanProperty estUtilisé = new SimpleBooleanProperty();
        private BooleanProperty showing = new SimpleBooleanProperty();
        private BooleanProperty freeze = new SimpleBooleanProperty();

        public Curseur(double x, double y, boolean estUtilisé) {
            setX(x);
            setY(y);
            this.estUtilisé.set(estUtilisé);
        }

        public double getX() {
            return x.get();
        }

        public DoubleProperty xProperty() {
            return x;
        }

        public void setX(double x) {
            this.x.set(x);
        }

        public double getY() {
            return y.get();
        }

        public DoubleProperty yProperty() {
            return y;
        }

        public void setY(double y) {
            this.y.set(y);
        }

        public boolean isEstUtilisé() {
            return estUtilisé.get();
        }

        public void setEstUtilisé(boolean estUtilisé) {
            this.estUtilisé.set(estUtilisé);
        }

        public BooleanProperty estUtiliséProperty() {
            return estUtilisé;
        }

        public boolean isShowing() {
            return showing.get();
        }

        public BooleanProperty showingProperty() {
            return showing;
        }

        public void setShowing(boolean showing) {
            this.showing.set(showing);
        }

        public boolean isFreeze() {
            return freeze.get();
        }

        public BooleanProperty freezeProperty() {
            return freeze;
        }

        public void setFreeze(boolean freeze) {
            this.freeze.set(freeze);
        }
    }

    /**
     * Custom line chart to include cross hair on plot area.
     *
     * @param <X>
     * @param <Y>
     */
    private class CrossHairLineChart<X, Y> extends LineChart {

        private Line vLine;
        private Line hLine;
        private Group plotArea;
        private BooleanProperty showFlag = new SimpleBooleanProperty();
        private BooleanProperty showCrossHair = new SimpleBooleanProperty();
        private BooleanProperty freeze = new SimpleBooleanProperty();
        private double tickSize = 5;
        private Curseur cursor;

        public CrossHairLineChart(Axis<X> xAxis, Axis<Y> yAxis) {
            super(xAxis, yAxis);
            vLine = new Line();
            hLine = new Line();

            hLine.endYProperty().bind(hLine.startYProperty());
            vLine.endXProperty().bind(vLine.startXProperty());
            vLine.visibleProperty().bind(showFlag.or(freeze));
            hLine.visibleProperty().bind(showFlag.or(freeze));
            setOnMouseExited(e -> {
                if (isShowCrossHair() && !isFreeze()) {
                    showFlag.set(false);
                }
            });
            setOnMouseMoved(e -> {
                if (isShowCrossHair() && plotArea != null && !isFreeze()) {
                    // If the mouse cursor is within the plot area bounds
                    if (isWithinPlotArea(e)) {
                        showFlag.set(true);
                        moveCrossHair(e);
                    } else {
                        showFlag.set(false);
                    }
                }
            });
            setOnMouseClicked(e -> {
                if (isShowCrossHair() && isWithinPlotArea(e)) {
                    freeze.set(!isFreeze());
                    if (!isFreeze()) {
                        moveCrossHair(e);
                    }
                }
            });
        }

        private boolean isWithinPlotArea(MouseEvent e) {
            Bounds b = plotArea.getBoundsInLocal();
            return b.getMinX() < e.getX() && e.getX() < b.getMaxX() && b.getMinY() < e.getY() && e.getY() < b.getMaxY();
        }

        public void setCursor(Curseur cursor) {
            this.cursor = cursor;
            showCrossHair.set(cursor.isEstUtilisé());
            cursor.estUtiliséProperty().addListener((obs, old, show) -> showCrossHair.set(show));
            cursor.xProperty().addListener((obs, old, xVal) -> vLine.setStartX(xVal.doubleValue()));
            cursor.yProperty().addListener((obs, old, yVal) -> hLine.setStartY(yVal.doubleValue()));
            showFlag.bindBidirectional(cursor.showingProperty());
            freeze.bindBidirectional(cursor.freezeProperty());
        }

        private void moveCrossHair(MouseEvent e) {
            Bounds b = plotArea.getBoundsInLocal();
            double x = e.getX() - b.getMinX() - tickSize;
            double y = e.getY() - b.getMinY() - tickSize;

            vLine.setStartX(x);
            hLine.setStartY(y);
            if (cursor != null) {
                cursor.setX(x);
                cursor.setY(y);
            }
        }

        public boolean isShowCrossHair() {
            return showCrossHair.get();
        }

        public BooleanProperty showCrossHairProperty() {
            return showCrossHair;
        }

        public void setShowCrossHair(boolean showCrossHair) {
            this.showCrossHair.set(showCrossHair);
        }

        public boolean isFreeze() {
            return freeze.get();
        }

        @Override
        protected void layoutPlotChildren() {
            super.layoutPlotChildren();
            if (plotArea == null && !getPlotChildren().isEmpty()) {
                Group plotContent = (Group) ((Node) getPlotChildren().get(0)).getParent();
                plotArea = (Group) plotContent.getParent();
            }
            if (!getPlotChildren().contains(vLine)) {
                getPlotChildren().addAll(vLine, hLine);
            }
            hLine.setStartX(0);
            hLine.setEndX(getBoundsInLocal().getWidth());

            vLine.setStartY(0);
            vLine.setEndY(getBoundsInLocal().getHeight());
        }
    }
}

关于java - 如何修复折线图十字准线的位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56222474/

相关文章:

java - JavaFX 中的 NullPointerException 在 TableView 中初始化

javascript - 使用 Sencha Ext-JS 4 图表拖放?

java - 我如何绘制 Java 中非常小的值?

c# - Microsoft 图表控件 - 失败后重绘图表(红叉)

java - 在标准 JAXB2 POJO 中绑定(bind)嵌套元素

eclipse - 如何在 Eclipse 中使用 fxml?

java - 如何从 javafx 中的文本流中删除文本?

java - Java 中 Jalali 日历的一个好的日期转换器?

java - 如何使用java查找特定日期之后发生的某个特定日期的下一个日期?

java - Cucumber测试到底是什么,是关于服务接口(interface)还是功能?是否需要连接DB才能获取真实数据?