在我的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方法将其提供给其他控制器。

09-25 20:15