如何在图表右侧创建展示xSpin与ySpin值的表格?
在图表右侧添加数据表格的实现方案
要在图表右侧展示xSpin和ySpin输入的x、y值,用JavaFX的TableView是最适配的选择——它能直接绑定数据集合,和图表的XYSeries同步更新,比手动维护GridPane文本框更高效可靠。
实现步骤
- 定义数据模型类
DataPoint,封装x、y值,用JavaFX属性类型实现双向绑定 - 创建
ObservableList<DataPoint>作为表格数据源,与XYSeries联动更新 - 配置
TableView的两列,分别对应x、y值的显示 - 将
TableView嵌入布局的右侧区域,调整窗口尺寸适配显示
修改后的完整代码
package Grava; import javafx.application.Application; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.image.Image; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.ToolBar; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.jfree.data.statistics.Regression; import org.jfree.fx.ChartViewer; public class ScatterAdd extends Application { private final XYSeries series = new XYSeries("Voltage"); private final XYSeries trend = new XYSeries("Trend"); private final XYSeriesCollection dataset = new XYSeriesCollection(series); // 表格数据源,存储所有输入的x、y值 private final ObservableList<DataPoint> dataPoints = FXCollections.observableArrayList(); ChoiceBox<String> domainLabels = new ChoiceBox<>(); ChoiceBox<String> rangeLabels = new ChoiceBox<>(); // 数据模型类,封装x、y值 public static class DataPoint { private final DoubleProperty x = new SimpleDoubleProperty(); private final DoubleProperty y = new SimpleDoubleProperty(); public DataPoint(double x, double y) { this.x.set(x); this.y.set(y); } public double getX() { return x.get(); } public DoubleProperty xProperty() { return x; } public double getY() { return y.get(); } public DoubleProperty yProperty() { return y; } } private JFreeChart createChart() { return ChartFactory.createScatterPlot("VI Characteristics", "Current", "Voltage", dataset); } @Override public void start(Stage stage) { Image image = new Image("Grava.logo.png"); stage.getIcons().add(image); var chart = createChart(); XYPlot plot = chart.getXYPlot(); plot.setDomainCrosshairVisible(true); plot.setRangeCrosshairVisible(true); XYLineAndShapeRenderer r = (XYLineAndShapeRenderer) plot.getRenderer(); r.setSeriesLinesVisible(1, Boolean.TRUE); r.setSeriesShapesVisible(1, Boolean.FALSE); var equation = new TextField(); series.addChangeListener((event) -> { if (series.getItemCount() > 1) { double[] coefficients = Regression.getOLSRegression(dataset, 0); double b = coefficients[0]; // 截距 double m = coefficients[1]; // 斜率 equation.setText("y = " + m + " x + " + b); double x = series.getDataItem(0).getXValue(); trend.clear(); trend.add(x, m * x + b); x = series.getDataItem(series.getItemCount() - 1).getXValue(); trend.add(x, m * x + b); // 避免重复添加趋势线 if (!dataset.getSeries().contains(trend)) { dataset.addSeries(trend); } } }); domainLabels.getSelectionModel().selectedItemProperty().addListener((ov, s0, s1) -> { chart.getXYPlot().getDomainAxis().setLabel(s1); }); rangeLabels.getSelectionModel().selectedItemProperty().addListener((ov, s0, s1) -> { chart.getXYPlot().getRangeAxis().setLabel(s1); }); domainLabels.getItems().addAll("Current", "Seconds"); domainLabels.setValue("Current"); rangeLabels.getItems().addAll("Voltage", "Metres"); rangeLabels.setValue("Voltage"); var xSpin = new Spinner<Double>(-10000000.000, 10000000.000, 0, 0.1); xSpin.setEditable(true); xSpin.setPromptText("Xvalue"); var ySpin = new Spinner<Double>(-10000000.000, 10000000.000, 0, 0.1); ySpin.setEditable(true); ySpin.setPromptText("Yvalue"); var button = new Button("Add"); button.setOnAction(ae -> { double xVal = xSpin.getValue(); double yVal = ySpin.getValue(); // 同时添加到图表series和表格数据源 series.add(xVal, yVal); dataPoints.add(new DataPoint(xVal, yVal)); }); HBox xBox = new HBox(); xBox.getChildren().addAll(domainLabels); HBox yBox = new HBox(); yBox.getChildren().addAll(rangeLabels); var enter = new ToolBar(xBox, xSpin, yBox, ySpin, button, equation); BorderPane.setAlignment(enter, Pos.CENTER); // 配置表格 TableView<DataPoint> tableView = new TableView<>(dataPoints); tableView.setPrefWidth(200); // 创建x列 TableColumn<DataPoint, Double> xColumn = new TableColumn<>("X值"); xColumn.setCellValueFactory(cellData -> cellData.getValue().xProperty().asObject()); xColumn.setPrefWidth(100); // 创建y列 TableColumn<DataPoint, Double> yColumn = new TableColumn<>("Y值"); yColumn.setCellValueFactory(cellData -> cellData.getValue().yProperty().asObject()); yColumn.setPrefWidth(100); tableView.getColumns().addAll(xColumn, yColumn); BorderPane root = new BorderPane(); root.setCenter(new ChartViewer(chart)); root.setBottom(enter); root.setRight(tableView); // 将表格放到右侧 stage.setTitle("ScatterAdd"); stage.setScene(new Scene(root, 920, 480)); // 调整窗口宽度,容纳表格 stage.show(); } public static void main(String[] args) { launch(args); } }
关键说明
- 数据模型
DataPoint:用JavaFX的DoubleProperty实现属性绑定,表格能自动感知数据变化并更新 - 同步更新逻辑:点击Add按钮时,同时把值添加到
XYSeries和ObservableList<DataPoint>,保证图表与表格数据完全一致 - 布局适配:将
TableView放到BorderPane的right区域,同时调整窗口宽度确保表格完整显示 - 趋势线优化:添加判断逻辑避免重复将趋势线添加到数据集,防止图表异常
内容的提问来源于stack exchange,提问作者armitageshanks99
相关产品推荐
相关产品推荐

