在我的JavaFx应用程序中,无论何时单击“计算”按钮,我都希望更新我的BarChart。问题是我得到:
java.lang.NullPointerException在
application.StatController.setActivityData(StatController.java:47)
它始终指向:xAxis.setCategories(optionsNames);
但是它在列表上有元素(请参见printscreen:https://image.ibb.co/b9YO8Q/Capture.png
在我的StatController类中,有setActivityData,它从FormController类中调用。
StatController类:
public class StatController {
@FXML
private BarChart<String, Double> barChart;
@FXML
private CategoryAxis xAxis;
Activities activities = new Activities();
private Map<String, List<Double>> uniqueActivityOptions = new HashMap<>();
private ObservableList<String> optionsNames = FXCollections.observableArrayList();
public StatController(){}
@FXML
private void initialize() {
}
public void setActivityData(Activities activitiesList) {
for(Activity activity : activities.getActivityList()) {
String optionName = activity.getOption();
if(uniqueActivityOptions.containsKey(optionName)) {
uniqueActivityOptions.get(optionName).add((double) activity.getNumber());
} else {
List<Double> activityOptionList = new ArrayList<>();
activityOptionList.add((double) activity.getNumber());
uniqueActivityOptions.put(optionName, activityOptionList);
}
}
for (Map.Entry<String, List<Double>> entry : uniqueActivityOptions.entrySet()) {
optionsNames.add(entry.getKey());
}
xAxis.setCategories(optionsNames);
XYChart.Series<String, Double> series = new XYChart.Series<>();
for (Map.Entry<String, List<Double>> entry : uniqueActivityOptions.entrySet()) {
Double average = calculateAverage(entry.getValue());
series.getData().add(new XYChart.Data<>(entry.getKey().toString(), average));
}
barChart.getData().add(series);
}
private double calculateAverage(List<Double> values) {
double result = 0;
for (Double value : values) {
result += value;
}
return result / values.size();
}
}
FormController类:
public class FormController {
Activities act = new Activities();
List<Activity> activities = act.getActivityList();
private ObservableList<String> opt = FXCollections.observableArrayList(
"Option 1",
"Option 2",
"Option 3"
);
@FXML
private Button calculateButton;
@FXML
private TextField numberField;
@FXML
private ComboBox<String> options;
private String selectedOption;
@FXML
private void initialize() {
options.getItems().addAll(opt);
options.getSelectionModel().selectedItemProperty()
.addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
selectedOption = newValue;
}
});
}
@FXML
public void calculateButtonClicked(){
activities.add(new Activity(selectedOption, Integer.parseInt(numberField.getText())));
StatController sc = new StatController();
sc.setActivityData(act);
}
}
我在StatsController中测试了setActivityData方法,并且在传递Activity时它可以正常工作。
请告知要更改的代码以传递和更新BarChart。
我知道这很琐碎,但我真的不知道该怎么做。
`
非常感谢你的帮助!
最佳答案
您的问题出在FormController#calculateButtonClicked()
中。您手动创建一个新的StatController
实例,然后调用setActivityData()
方法。这不是JavaFX的工作方式,并且会导致xAxis
成员为null,因此您的NullPointerException
。
调用@FXML
方法时,由FXMLLoader
类注入带有FXMLLoader#load()
注释的控制器成员。您将需要使用FXMLLoader
类加载与StatController
类相对应的.fxml文件,然后该文件将为您创建StatController
对象的实例,并且还将注入xAxis和barChart实例。
这是一个快速(尚未准备好投入生产)的示例,您将必须根据自己的特定情况进行调整:
FXMLLoader loader = new FXMLLoader();
loader.setLocation(filePath); // your path to the .fxml file
loader.load();
StatController controller = (StatController) loader.getController();
controller.setActivityData(activities); // your activities data
如何以及何时进行操作取决于场景图的设置方式。查看您的源代码,我建议进行以下更改:
MainController.java
public class MainController
implements Initializable {
@FXML
private TabPane tabPane;
@FXML
private Tab formTabPage;
@FXML
private FormController formTabController; // change name to this
@FXML
private Tab statsTabPage;
@FXML
private StatController statsTabController; // change name to this
private MainApp mainApp;
public void setMainApp(MainApp mainApp) {
this.mainApp = mainApp;
}
@Override // rename your init() method to this
public void initialize(URL location, ResourceBundle resources) {
// add this line
formTabController.setStatController(statTabController);
}
}
FormController.java
public class FormController {
Activities act = new Activities();
List<Activity> activities = act.getActivityList();
private ObservableList<String> opt = FXCollections.observableArrayList(
"Option 1",
"Option 2",
"Option 3"
);
@FXML
private Button calculateButton;
@FXML
private TextField numberField;
@FXML
private ComboBox<String> options;
private String selectedOption;
private StatController statController; // add this member
@FXML
private void initialize() {
options.getItems().addAll(opt);
options.getSelectionModel().selectedItemProperty()
.addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
selectedOption = newValue;
}
});
}
@FXML
public void calculateButtonClicked() {
// change this method here
statController.setActivityData(act);
}
// add this method here
public void setStatController(StatController statController) {
this.statController = statController;
}
}
还会发生其他问题,但这可以解决您的空指针异常。之所以可行,而您先前的代码却没有,是因为您正在手动创建
StatController
的新实例,而不是使用FXMLLoader
已经为您加载并映射到用户界面节点的实例。我在上面所做的所有工作都是捕获对所需控制器的引用,并通过setter方法将其提供给其他控制器。