我一直在遵循有关javafx的教程,该教程由一个简单的地址簿组成,一切正常,直到我尝试创建一个新阶段,在该阶段中,我必须填充文本字段以创建新Person或编辑现有文本人,但是当我单击添加按钮或编辑按钮时,新的舞台没有出现,并且我收到了NullPointerException:
java.lang.NullPointerException
at com.nouh.address.view.PersonEditDialogController.setPerson(PersonEditDialogController.java:72)
at com.nouh.address.MainApp.showPersonEditDialog(MainApp.java:144)
at com.nouh.address.view.PersonOverviewController.handleAdd(PersonOverviewController.java:159)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76)
at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at javafx.base/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:275)
at javafx.fxml/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83)
at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1784)
at javafx.fxml/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670)
at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
at javafx.graphics/javafx.scene.Node.fireEvent(Node.java:8863)
at javafx.controls/javafx.scene.control.Button.fire(Button.java:200)
at javafx.controls/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206)
at javafx.controls/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3876)
at javafx.graphics/javafx.scene.Scene$MouseHandler.access$1300(Scene.java:3604)
at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1874)
at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2613)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433)
at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(View.java:556)
at javafx.graphics/com.sun.glass.ui.View.notifyMouse(View.java:942)
at javafx.graphics/com.sun.glass.ui.mac.MacView.notifyMouse(MacView.java:127)
**我正在使用的类是:**
package com.nouh.address;
import com.nouh.address.model.Person;
import com.nouh.address.view.PersonEditDialogController;
import com.nouh.address.view.PersonOverviewController;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
public class MainApp extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
/**
* The data as an observable list of Persons.
*/
private ObservableList<Person> personData =
FXCollections.observableArrayList();
//constructor to add some simple data to the observable
public MainApp(){
personData.add(new Person("Hans", "Muster"));
.... //some dummy data
}
public static void main(String []args){
launch(args);
}
@Override
public void start(Stage primaryStage){
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Address App");
initRootLayout();
showPersonOverview();
}
private void initRootLayout(){
try{
//1- load the fxml file :
FXMLLoader loader = new FXMLLoader();
//2- set the relative path to the fxml file we want to load :
loader.setLocation(MainApp.class.getResource("view/RootLayout.fxml"));
//3-load the UI :
this.rootLayout = (BorderPane)loader.load();
//4- create a scene and then set the UI elements we've loaded on the that scene
Scene scene = new Scene(this.rootLayout);
//5- put the scene inside the window
this.primaryStage.setScene(scene);
//6. show the stage
this.primaryStage.show();
}catch (IOException fxmlLoaderError){
System.out.println("ioE : fxml \n"+fxmlLoaderError.getMessage());
}
}
private void showPersonOverview(){
try {
// Load person overview.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/PersonOverview.fxml"));
AnchorPane pane = (AnchorPane)loader.load();
this.rootLayout.setCenter(pane);
// Give the controller access to the main app.
PersonOverviewController controller = loader.getController();
controller.setMainApp(this);
}catch (IOException fxmlLoaderError){
System.out.println("ioE : fxml
\n"+fxmlLoaderError.getMessage());
}
}
public Stage getPrimaryStage() {
return primaryStage;
}
public ObservableList<Person> getPersonData() {
return personData;
}
public void setPersonData(ObservableList<Person> personData) {
this.personData = personData;
}
/**
* Opens a dialog to edit details for the specified person. If the user
* clicks OK, the changes are saved into the provided person object and true
* is returned.
*
* @param person the person object to be edited
* @return true if the user clicked OK, false otherwise.
*/
public boolean showPersonEditDialog(Person person){
try {
//load
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/PersonEditDialog.fxml"));
AnchorPane editRootLayout = (AnchorPane)loader.load();
//create the stage;
Stage dialogStage = new Stage();
dialogStage.setTitle("Edit Person");
dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.initOwner(primaryStage);
Scene scene = new Scene(editRootLayout);
dialogStage.setScene(scene);
// Set the person into the controller.
PersonEditDialogController controller = new PersonEditDialogController();
controller.setDialogStage(dialogStage);
controller.setPerson(person);
//show and wait :
dialogStage.showAndWait();
return controller.isOkClicked();
}catch (IOException e){
System.out.println("can't load the edit dialog");
return false;
}catch (NullPointerException e){
System.out.println(e.getMessage()+" : here is the error");
e.printStackTrace();
return false;
}
}
}
模型类:
package com.nouh.address.model;
import javafx.beans.property.*;
import java.time.LocalDate;
public class Person {
private final StringProperty firstname;
private final StringProperty lastname;
private final StringProperty city;
private final StringProperty street;
private final StringProperty email;
private final IntegerProperty postalCode;
private final ObjectProperty<LocalDate> birthdate;
public Person(String firstname,String lastname){
this.firstname = new SimpleStringProperty(firstname);
this.lastname = new SimpleStringProperty(lastname);
//default data
this.street = new SimpleStringProperty("some street");
this.city = new SimpleStringProperty("some beautiful city");
this.email = new SimpleStringProperty("myemail@google.com");
this.postalCode = new SimpleIntegerProperty(123456789);
this.birthdate = new SimpleObjectProperty<LocalDate>(LocalDate.of(2000,12,12));
}
public Person(){
this("","");
}
.....//getters and setters around here
public void setBirthdate(LocalDate birthdate) {
this.birthdate.set(birthdate);
}
}
控制者: PersonOverviewController:
package com.nouh.address.view;
import com.nouh.address.MainApp;
import com.nouh.address.model.Person;
import com.nouh.address.util.DateUtil;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
public class PersonOverviewController {
@FXML
private TableView<Person> personTable;
@FXML
private TableColumn<Person,String> firstname_col;
@FXML
private TableColumn<Person,String> lastname_col;
@FXML
private Label firstname;
@FXML
private Label lastname;
...
@FXML
private Button add;
@FXML
private Button edit;
@FXML
private Button delete;
//reference to the main app
private MainApp mainApp;
/**
* The constructor.
* The constructor is called before the initialize() method.
*/
public PersonOverviewController(){
}
/**
* Initializes the controller class. This method is automatically called
* after the fxml file has been loaded.
*/
@FXML
private void initialize(){
//Initialize the person table with the two columns.
this.firstname_col.setCellValueFactory(cellData-> cellData.getValue().firstnameProperty());
this.lastname_col.setCellValueFactory(cellData-> cellData.getValue().lastnameProperty());
//clear the person Details :
clearFields();
//add a change Listener : Listen for selection changes and show the person details when changed.
personTable.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Person>() {
@Override
public void changed(ObservableValue<? extends Person> observable, Person oldValue, Person newValue) {
showPersonDetails(newValue);
}
});
/*
this code is equivalent :
personTable.getSelectionModel().selectedItemProperty().addListener(
(observable , oldValue , newValue) -> showPersonDetails(newValue)
);
*/
}
/**
* Is called by the main application to give a reference back to itself.
*
* @param mainApp
*/
public void setMainApp(MainApp mainApp){
this.mainApp = mainApp;
//add observable liste to the table of person :
this.personTable.setItems(mainApp.getPersonData());
}
/**
* Fills all text fields to show details about the person.
* If the specified person is null, all text fields are cleared.
*
* @param person the person or null
*/
private void showPersonDetails(Person person){
if(person != null){
firstname.setText(person.getFirstname());
lastname.setText(person.getLastname());
city.setText(person.getCity());
street.setText(person.getCity());
postalcode.setText(Integer.toString(person.getPostalCode()));
// TODO: We need a way to convert the birthday into a String!
birthdate.setText(DateUtil.format(person.getBirthdate()));
}else {
clearFields();
}
}
private void clearFields(){
firstname.setText("");
lastname.setText("");
street.setText("");
city.setText("");
birthdate.setText("");
postalcode.setText("");
email.setText("");
}
/**
* Called when the user clicks on the delete button.
*/
@FXML
private void handleDeletePerson(){
int indxOfSelectedPerson = personTable.getSelectionModel().getSelectedIndex();
if(indxOfSelectedPerson >= 0){
personTable.getItems().remove(indxOfSelectedPerson);
refresh();
}else {
showError("");
}
}
/**
* Called when the user clicks the new button. Opens a dialog to edit
* details for a new person.
*/
@FXML
private void handleAdd(){
Person tempPerson = new Person();
boolean okClicked = mainApp.showPersonEditDialog(tempPerson);
if(okClicked){
mainApp.getPersonData().add(tempPerson);
}
}
/**
* Called when the user clicks the edit button. Opens a dialog to edit
* details for the selected person.
*/
@FXML
private void handleEditPerson(){
Person selectedPerson = personTable.getSelectionModel().getSelectedItem();
if(selectedPerson != null){
boolean okClicked = mainApp.showPersonEditDialog(selectedPerson);
if(okClicked){
showPersonDetails(selectedPerson);
}
}else {
showError("please select a person to be edited");
}
}
private void refresh(){
personTable.getSelectionModel().clearSelection();
}
private void showError(String error){
Alert alert = new Alert(AlertType.WARNING);
alert.initOwner(mainApp.getPrimaryStage());
alert.setTitle("No Selection");
alert.setHeaderText("No Pesron Selected");
alert.setContentText("please selecet a person from the table to delete"+error);
alert.showAndWait();
}
}
PersonEditDialogController:
package com.nouh.address.view;
import com.nouh.address.model.Person;
import com.nouh.address.util.DateUtil;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import javafx.scene.control.Alert.AlertType;
public class PersonEditDialogController {
@FXML
private TextField firstname_txt;
@FXML
private TextField lastname_txt;
@FXML
private TextField birthdate_txt;
@FXML
private TextField city_txt;
@FXML
private TextField street_txt;
@FXML
private TextField postalcode_txt;
@FXML
private Button cancel_btn;
@FXML
private Button ok_btn;
private Stage dialogStage;
private Person person;
private boolean OkClicked = false;
/**
* Initializes the controller class. This method is automatically called
* after the fxml file has been loaded.
*/
@FXML
private void initialize(){
}
/**
* Sets the stage of this dialog.
*
* @param dialogStage
*/
public void setDialogStage(Stage dialogStage){
this.dialogStage = dialogStage;
}
/**
* Sets the person to be edited in the dialog.
*
* @param person
*/
public void setPerson(Person person) {
this.person = person;
if(person != null) {
firstname_txt.setText(this.person.getFirstname());
lastname_txt.setText(person.getLastname());
birthdate_txt.setText(DateUtil.format(person.getBirthdate()));
birthdate_txt.setPromptText("dd.mm.yy");
street_txt.setText(person.getStreet());
city_txt.setText(person.getCity());
postalcode_txt.setText(Integer.toString(person.getPostalCode()));
}
}
public boolean isOkClicked() {
return OkClicked;
}
@FXML
private void handleOK(){
if(isInputValid()){
person.setFirstname(firstname_txt.getText());
person.setLastname(lastname_txt.getText());
person.setCity(city_txt.getText());
person.setStreet(street_txt.getText());
person.setPostalCode(Integer.parseInt(postalcode_txt.getText()));
person.setBirthdate(DateUtil.parse(birthdate_txt.getText()));//look if it is a valid date?
OkClicked = true;
dialogStage.close();
}
}
@FXML
private void handleCancel(){
this.dialogStage.close();
}
private boolean isInputValid(){
String errorMessage = "";
if(firstname_txt.getText() == null || firstname_txt.getText().length() == 0){
errorMessage += "firstname can't be empty\n";
}
if(lastname_txt.getText() == null || lastname_txt.getText().length() == 0){
errorMessage += "lastname can't be empty\n";
}
if(city_txt.getText() == null || city_txt.getText().length() == 0){
errorMessage += "city can't be empty\n";
}
if(street_txt.getText() == null || street_txt.getText().length() == 0){
errorMessage += "street can't be empty\n";
}
if(postalcode_txt.getText() == null || postalcode_txt.getText().length() == 0){
errorMessage += "postal code can't be empty\n";
return false;
}else {
try {
int postal = Integer.parseInt(postalcode_txt.getText());
}catch (NumberFormatException e){
errorMessage += "postal code must be a positive number ";
}
}
if(birthdate_txt.getText() == null || birthdate_txt.getText().length() == 0){
errorMessage += "birthdate can't be empty\n";
}else {
if(!DateUtil.validDate(birthdate_txt.getText())){
errorMessage += "birthdate incorrect\n";
}
}
if(errorMessage.length() == 0)
return true;
showError(errorMessage);
return false;
}
private void showError(String error){
Alert alert = new Alert(AlertType.WARNING);
alert.initOwner(this.dialogStage);
alert.setTitle("invalid input");
alert.setHeaderText("Please correct the invalid fields");
alert.setContentText(error);
alert.showAndWait();
}
}
还有一个帮助器类:
package com.nouh.address.util;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
public class DateUtil {
/** The date pattern that is used for conversion. Change as you wish. */
private static final String DATE_PATTERN = "DD.MM.YYYY";
//the date formatter
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_PATTERN);
/**
* Returns the given date as a well formatted String. The above defined
* {@link DateUtil#DATE_PATTERN} is used.
*
* @param date the date to be returned as a string
* @return formatted string
*/
public static String format(LocalDate date){
if(date == null){
return null;
}
return DATE_FORMATTER.format(date);
}
/**
* Converts a String in the format of the defined {@link DateUtil#DATE_PATTERN}
* to a {@link LocalDate} object.
*
* Returns null if the String could not be converted.
*
* @param dateString the date as String
* @return the date object or null if it could not be converted
*/
public static LocalDate parse(String dateString){
try {
return DATE_FORMATTER.parse(dateString, LocalDate::from);
}catch (DateTimeParseException e){
return null;
}
}
/**
* Checks the String whether it is a valid date.
*
* @param dateString
* @return true if the String is a valid date
*/
public static boolean validDate(String dateString){
//parse and the check the values
return DateUtil.parse(dateString) != null;
//LocalDate date = DateUtil.parse(dateString);
// if( (date.getDayOfMonth() > 31) || (date.getDayOfMonth() < 0) || (date.getMonthValue()>12) || (date.getMonthValue() < 0))
//return false;
// return true;
}
}
我怀疑问题出在 MainApp.java
public boolean showPersonEditDialog(Person person){
try {
//load
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/PersonEditDialog.fxml"));
AnchorPane editRootLayout = (AnchorPane)loader.load();
//create the stage;
Stage dialogStage = new Stage();
dialogStage.setTitle("Edit Person");
dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.initOwner(primaryStage);
Scene scene = new Scene(editRootLayout);
dialogStage.setScene(scene);
// Set the person into the controller.
PersonEditDialogController controller = new PersonEditDialogController();
controller.setDialogStage(dialogStage);
controller.setPerson(person);
//show and wait :
dialogStage.showAndWait();
return controller.isOkClicked();
}catch (IOException e){
System.out.println("can't load the edit dialog");
return false;
}catch (NullPointerException e){
System.out.println(e.getMessage()+" : here is the error");
e.printStackTrace();
return false;
}
}
但我无法弄清楚,主要阶段和PersonOverView加载并正确显示,并且删除一个人也很好