我需要在单击 JavaFX BarChart 中的条形图时获取该条形的 X 和 Y 轴值,以便处理和保存此数据。使用 MVC 架构,我必须将包含 BarChart 的视图与存在 BarChart 条形事件处理程序的控制器分开。因此,我无法获取单击栏的 x 或 y 值,因为一旦进入处理程序,我就无法再检索单击栏。我看到有些人在监听器附加到栏的 for 循环中获取数据值,并使用它,但显然,这不会返回单击的栏,而是返回监听器附加的最后一个栏。那么,一旦我进入控制器的事件处理程序,如何检索单击的栏?控制器和视图代码如下。
控制器:
EventHandler<MouseEvent> statsBarButtonHandler = new EventHandler<>() {
@Override
public void handle(MouseEvent actionEvent) {
System.out.println("PASSO AI GENERI E ARTISTI");
StatisticsHandler statDomain = new StatisticsHandler();
mainStage.setScene(genreView);
}
};
for (XYChart.Data<Number, String> data : typeView.getTypeSerie().getData()) {
data.getNode().setOnMouseClicked(statsBarButtonHandler);
this.dataClicked = data;
}
条形图:
BarChart <Number, String> barChart = new BarChart<>(xAxis, yAxis);
XYChart.Series<Number, String> series = new XYChart.Series<>();
this.typeSerie=series;
//
series.getData().add(new XYChart.Data<>(0, "Category 1"));
series.getData().add(new XYChart.Data<>(14, "Category 2"));
series.getData().add(new XYChart.Data<>(72, "Category 3"));
series.getData().add(new XYChart.Data<>(55, "Category 4"));
series.getData().add(new XYChart.Data<>(100, "Category 5"));
// Aggiunta della serie al grafico
barChart.getData().add(series);
请记住,必须使用 MVC 架构,我不能将控制器放在视图中;他们必须保持分开。所以我需要找到一种方法来直接在控制器的处理程序中获取值。
正如我已经提到的,我尝试了在某些网站上找到的方法,从附加侦听器的 for 循环中获取单击的栏,如此处所示,但作为输出,我总是获得最后一个关联的类别,即 5,并且不是点击的那个。
private XYChart.Data dataClicked;
EventHandler<MouseEvent> statsBarButtonHandler = new EventHandler<>() {
@Override
public void handle(MouseEvent actionEvent) {
System.out.println("PASSO AI GENERI E ARTISTI");
StatisticsHandler statDominio = new StatisticsHandler();
System.out.println(dataClicked.getYValue());
}
};
for (XYChart.Data<Number, String> data : typeView.getTypeSerie().getData()) {
data.getNode().setOnMouseClicked(statsBarButtonHandler);
this.dataClicked = data;
}
在我看来(和 James_D 的),最好的解决方案是为每个数据的节点注册一个唯一的
EventHandler
。这将使您知道单击了哪个数据的节点,因为只有一个这样的节点可以触发处理程序。下面显示了这方面的示例,但不是在 MVC 的上下文中。
import java.util.List;
import java.util.Random;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(Main.class);
}
@Override
public void start(Stage primaryStage) {
var xAxis = new NumberAxis();
xAxis.setLabel("X");
var yAxis = createCategoryAxis();
var chart = new BarChart<>(xAxis, yAxis);
chart.getData().add(createSeries(yAxis.getCategories()));
primaryStage.setScene(new Scene(chart, 600, 400));
primaryStage.show();
}
private CategoryAxis createCategoryAxis() {
var yAxis = new CategoryAxis();
yAxis.setLabel("Y");
for (int i = 1; i <= 5; i++) {
yAxis.getCategories().add("Category " + i);
}
return yAxis;
}
private XYChart.Series<Number, String> createSeries(List<String> categories) {
var random = new Random();
var series = new XYChart.Series<Number, String>();
series.setName("Series");
for (var category : categories) {
var data = new XYChart.Data<Number, String>(random.nextInt(1, 20), category);
// Note 'subscribe' was added in JavaFX 21. If using an older version you can do
// something similar with a ChangeListener (though note the Consumer passed to
// 'subscribe' will be notified of the current value immediately, a ChangeListener
// will not).
data.nodeProperty().subscribe((oldNode, newNode) -> {
// Stop listening to the old node to prevent potential memory leaks.
if (oldNode != null) oldNode.setOnMouseClicked(null);
if (newNode != null) {
// Unique EventHandler for each XYChart.Data instance. This makes it easy to know
// which XYChart.Data's node was clicked, since the instance is captured.
newNode.setOnMouseClicked(e -> {
e.consume();
var x = data.getXValue();
var y = data.getYValue();
System.out.printf("Data node clicked: '%s' = %d%n", y, x);
});
}
});
series.getData().add(data);
}
return series;
}
}