JAVAFX:控制器 class 中的字段值未保存 correctly/trouble 更新列表视图?
JAVAFX: Field value in controller class isn't saved correctly/trouble updating listview?
所以我正在用JavaFX制作一个小程序来熟悉它。
我很确定我对 javafx 的工作原理有一些基本的了解,但我似乎无法理解,因为我遇到了一些我找不到解决方案的问题。尽管在 google.
上对它们进行了彻底研究
目前我有一个使用 eclipse 和 scenebuilder 1.1 的设置(因为 2.0 给我带来了各种麻烦)。
我有一个非常简单的设置,主应用程序加载初始主应用程序 window。
public class Main extends Application {
@Override
public void start(final Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/application/ApplicationWindow.fxml"));
Scene scene = new Scene(root);
stage.getIcons().add(new Image("/application/bug3.png"));
stage.setResizable(true);
stage.setTitle("Simple Bugtracker");
stage.setMinHeight(500);
stage.setMinWidth(800);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) { launch(args); }
}
它似乎工作正常。然后我创建了一个控制器 class,它包含正确的 fx:id'et gui 元素作为字段值,它工作正常,我可以用 java 代码来操作它们。
不过到目前为止我遇到了两个问题。
1) 当我从我的一个列表视图中删除某些内容时,我设法解决了更新问题,因此它通过添加一个 if null 场景立即将其从列表视图中删除。但是当我创建我的对象类型的新实例时,列表视图直到我重新启动程序才更新?
2) 其次,我在控制器 class 中遇到了一个简单的 int 字段值问题,我想在即时调用的方法中使用它的当前值(在 scenebuilder 中的按钮上设置),但是它似乎总是坚持认为即使值设置正确,它仍然是启动期间实例化时的原始值。
这是我的相当大的控制器 class。
public class MyController implements Initializable {
// Logic related fields
public ProjectList<Project> projectList = new ProjectList<Project>();
public int currentProjectIndex = -1;
public Bug currentBug;
// Listview buffers
public ObservableList<Project> projectsListBuffer = FXCollections.observableArrayList();
public ObservableList<Bug> unsolvedListBuffer = FXCollections.observableArrayList();
public ObservableList<Bug> solvedListBuffer = FXCollections.observableArrayList();
// GUI related fields
@FXML public ListView<Project> projectsListView;
@FXML public ListView<Bug> unsolvedListView;
@FXML public ListView<Bug> solvedListView;
@FXML public TextArea topDisplayArea;
@FXML public Button btnCreateProject;
@FXML public TextField titleFieldCreateProject;
@FXML public TextArea descriptionAreaCreateProject;
@FXML public AnchorPane createProjectWindow;
@FXML public AnchorPane projectsListViewAnchor;
@FXML public Label projectTitleLabel;
@FXML public Button createBugButton;
@FXML public TextField titleFieldCreateBug;
@FXML public TextArea descriptionAreaCreateBug;
public Stage createProjectStage;
// public Stage createBugStage;
@Override
public void initialize(URL arg0, ResourceBundle arg1) {
// load data from file system
try {
loadData();
} catch (IOException e) {
e.printStackTrace();
}
// Instantiate gui field values
// projectsListView = new ListView();
// unsolvedListView = new ListView();
// solvedListView = new ListView();
//
//
// topDisplayArea = new TextArea();
// btnCreateProject = new Button();
//
// titleFieldCreateProject = new TextField();
// descriptionAreaCreateProject = new TextArea();
//
// createProjectWindow = new AnchorPane();
// projectsListViewAnchor = new AnchorPane();
//
// projectTitleLabel = new Label();
// createBugButton = new Button();
//
// titleFieldCreateBug = new TextField();
// descriptionAreaCreateBug = new TextArea();
updateListBuffers();
projectsListView.setPrefHeight( getScreenH() );
projectsListView.setItems(projectsListBuffer);
unsolvedListView.setPrefHeight( getScreenH() );
unsolvedListView.setItems(unsolvedListBuffer);
// settings on necessary gui items
topDisplayArea.setPrefHeight( getScreenH() );
topDisplayArea.setPrefWidth( getScreenW() );
topDisplayArea.setEditable(false);
topDisplayArea.setBackground(null); // something to be done here for transparent logo in background of all project descriptions etc.
// ved dobbeltklik i projektlisten, vælg project og sæt titel
projectsListView.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent click) {
if (click.getClickCount() == 2) {
//Use ListView's getSelected Item
currentProjectIndex = projectsListView.getSelectionModel().getSelectedIndex();
Project currentProject = projectList.get(projectsListView.getSelectionModel().getSelectedIndex());
projectTitleLabel.setText(currentProject.getTitle());
System.out.println( "Selected project:"+currentProject.getTitle());
solvedListView.setItems(unsolvedListBuffer);
for (Bug b : currentProject.solvedBugs) {
System.out.println(b.getTitle()+"\n"+b.getErrorDescription());
}
updateListBuffers();
}
}
});
// ved dobbeltklik i uløste bugs listen, sæt top display område
unsolvedListView.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent click) {
if (click.getClickCount() == 2) {
//Use ListView's getSelected Item
currentBug = unsolvedListView.getSelectionModel().getSelectedItem();
topDisplayArea.setText(currentBug.getErrorDescription());
}
}
});
solvedListView.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent click) {
if (click.getClickCount() == 2) {
//Use ListView's getSelected Item
currentBug = solvedListView.getSelectionModel().getSelectedItem();
topDisplayArea.setText(currentBug.getErrorDescription());
}
}
});
// custom listview cell for the projects listview
projectsListView.setCellFactory(new Callback<ListView<Project>, ListCell<Project>>() {
@Override
public ListCell<Project> call(ListView<Project> p) {
ListCell<Project> cell = new ListCell<Project>() {
@Override
protected void updateItem(Project t, boolean bln) {
super.updateItem(t, bln);
if (t != null) {
// program a custom cell with several items in it
Text text = new Text();
Text text2 = new Text();
text.wrappingWidthProperty().bind(p.widthProperty().subtract(15));
text.setText(t.getTitle());
text2.wrappingWidthProperty().bind(p.widthProperty().subtract(15));
text2.setText(t.getCreationDate().toLocaleString());
text.setFont(Font.font ("Verdana", 12));
text2.setFont(Font.font ("Verdana", 10));
text.setFill(Color.BLACK);
text2.setFill(Color.TEAL);
AnchorPane pane = new AnchorPane();
pane.setPrefHeight(90);
pane.setPrefWidth(300);
ImageView imageView = new ImageView();
imageView.setImage(new Image("/application/project1.png"));
imageView.setFitHeight(60);
imageView.setFitWidth(60);
Image imageOk = new Image(getClass().getResourceAsStream("/application/selectionicon.png"));
Button deleteButton = new Button("Delete");
deleteButton.setLayoutX(233);
deleteButton.setLayoutY(60);
text.setLayoutX(70);
text.setLayoutY(30);
text2.setLayoutX(70);
text2.setLayoutY(50);
pane.getChildren().addAll(imageView, text, text2, deleteButton);
// delete actionevent
deleteButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Confirm deletion");
alert.setHeaderText("Are you sure you want to delete this project?");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK){
int selIndex = projectsListView.getSelectionModel().getSelectedIndex();
if (selIndex>-1) {
projectsListBuffer.remove(selIndex);
projectList.remove(selIndex);
projectsListView.setItems(projectsListBuffer);
projectList.save();
}
} else {
System.out.println("DENIED!!");
}
}
});
setPrefWidth(0);
setGraphic(pane);
} else {
setText(null);
setGraphic(null);
}
}
};
return cell;
}
});
// custom listview cell for the unsolved bugs listview
unsolvedListView.setCellFactory(new Callback<ListView<Bug>, ListCell<Bug>>() {
@Override
public ListCell<Bug> call(ListView<Bug> p) {
ListCell<Bug> cell = new ListCell<Bug>() {
@Override
protected void updateItem(Bug t, boolean bln) {
super.updateItem(t, bln);
if (t != null) {
// program a custom cell with several items in it
Text text = new Text();
Text text2 = new Text();
text.wrappingWidthProperty().bind(p.widthProperty().subtract(15));
text.setText(t.getTitle());
text2.wrappingWidthProperty().bind(p.widthProperty().subtract(15));
text2.setText(t.getCreationDate().toLocaleString());
text.setFont(Font.font ("Verdana", 12));
text2.setFont(Font.font ("Verdana", 10));
text.setFill(Color.BLACK);
text2.setFill(Color.TEAL);
AnchorPane pane = new AnchorPane();
pane.setPrefHeight(90);
pane.setPrefWidth(300);
ImageView imageView = new ImageView();
imageView.setImage(new Image("/application/bug3.png"));
imageView.setFitHeight(60);
imageView.setFitWidth(60);
Image imageOk = new Image(getClass().getResourceAsStream("/application/selectionicon.png"));
Button selectButton = new Button("Select");
Button deleteButton = new Button("Delete");
selectButton.setLayoutX(233);
selectButton.setLayoutY(60);
deleteButton.setLayoutX(180);
deleteButton.setLayoutY(60);
text.setLayoutX(70);
text.setLayoutY(30);
text2.setLayoutX(70);
text2.setLayoutY(50);
pane.getChildren().addAll(imageView, text, text2);
deleteButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
projectsListBuffer.remove(projectsListView.getSelectionModel().getSelectedIndex());
projectList.remove(projectsListView.getSelectionModel().getSelectedIndex());
projectsListView.setItems(projectsListBuffer);
projectList.save();
}
});
setPrefWidth(0);
setGraphic(pane);
} else {
setText(null);
setGraphic(null);
}
}
};
return cell;
}
});
}
// loads and show the create project window from it's fxml file (CreateProjectWindow.fxml)
public void showCreateProjectWindow() {
try {
Parent root1 = FXMLLoader.load(getClass().getResource("/application/CreateProjectWindow.fxml"));
createProjectStage = new Stage();
Scene scene = new Scene(root1);
createProjectStage.setScene(scene);
createProjectStage.setMinWidth(374);
createProjectStage.setMinHeight(416);
createProjectStage.setResizable(false);
createProjectStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
// actionevent method that creates a new project MANGLER AT FIKSE DET SÅ LISTVIEWET OPDATERER SIG MED DET SAMME
public void createProject() {
String title = titleFieldCreateProject.getText();
String description = descriptionAreaCreateProject.getText();
Project project = new Project(title, description, new Date());
projectList.add(project);
projectsListBuffer.add(project);
projectList.save();
updateListBuffers();
projectsListView.setItems(projectsListBuffer);
}
// loads and show the create project window from it's fxml file (CreateProjectWindow.fxml)
public void showCreateBugWindow() {
try {
Parent root1 = FXMLLoader.load(getClass().getResource("/application/CreateBugWindow.fxml"));
createProjectStage = new Stage();
Scene scene = new Scene(root1);
createProjectStage.setScene(scene);
createProjectStage.setMinWidth(374);
createProjectStage.setMinHeight(416);
createProjectStage.setResizable(false);
createProjectStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
// actionevent method that creates a new bug MANGLER AT FIKSE DET SÅ LISTVIEWET OPDATERER SIG MED DET SAMME
public void createBug() {
if (currentProjectIndex >-1) {
String title = titleFieldCreateBug.getText();
String description = descriptionAreaCreateBug.getText();
System.out.println(title+"\n"+description);
Bug bug = new Bug(title, description, new Date());
projectList.get(currentProjectIndex).unsolvedBugs.add(bug);
unsolvedListBuffer.add(bug);
projectList.save();
updateListBuffers();
} else {
System.out.println("Failed creation.");
}
}
// load, clear and reload buffers
public void updateListBuffers() {
if (currentProjectIndex>-1) {
unsolvedListBuffer.clear();
for (int i=0; i<projectList.get(currentProjectIndex).unsolvedBugs.size(); i++) {
unsolvedListBuffer.add( projectList.get(currentProjectIndex).unsolvedBugs.get(i) );
}
solvedListBuffer.clear();
for (int i=0; i<projectList.get(currentProjectIndex).solvedBugs.size(); i++) {
solvedListBuffer.add( projectList.get(currentProjectIndex).solvedBugs.get(i) );
}
// unsolvedListView.setItems(null);
// solvedListView.setItems(null);
unsolvedListView.setItems(unsolvedListBuffer);
solvedListView.setItems(solvedListBuffer);
}
projectsListBuffer.clear();
for (int i=0; i<projectList.size(); i++) {
projectsListBuffer.add( projectList.get(i) );
}
// projectsListView.setItems(null);
projectsListView.setItems(projectsListBuffer);
}
// file system related methods
public void loadData() throws IOException {
File fil = new File("Project_Data.dat");
if ( !fil.exists() ) {
fil.createNewFile();
projectList = new ProjectList<Project>();
projectList.save();
} else {
projectList = ProjectList.load();
}
}
// practical methods
public double getScreenH() {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
return screenSize.getHeight();
}
public double getScreenW() {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
return screenSize.getWidth();
}
}
我遇到问题的值是 currentProjectIndex,它似乎正确保存了它的值,但是当 "createBug()" 方法是 运行 单击按钮时,它说它仍然-1(与初始化时一样)?
关于第一个问题,如您所见,我正在调用 window 来创建新项目或错误,然后 运行 调用 createProject() 或 createBug() 方法,通过点击一个按钮。但这就像我 运行 某些方法时未保存控制器 (selectedProjectIndex) 中的字段值?
总之我可以成功创建项目,将它们保存到文件中。但是在程序重新启动之前列表视图不会更新。
其次,我不能创建任何错误,因为出于某种原因,selectedProjectIndex 始终被视为 -1?
编辑:使用新建议更新了控制器 class。
当我尝试使用 showCreateProjectWindow() 或 showCreateBugWindow() 方法打开新 window 时的堆栈跟踪。
/C:/Users/Giuseppe/Dropbox/Java/BugTracker/bin/application/CreateProjectWindow.fxml
at javafx.fxml.FXMLLoader.constructLoadException(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.load(Unknown Source)
at application.MyController.showCreateProjectWindow(MyController.java:320)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.reflect.misc.Trampoline.invoke(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.reflect.misc.MethodUtil.invoke(Unknown Source)
at javafx.fxml.FXMLLoader$MethodHandler.invoke(Unknown Source)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(Unknown Source)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source)
at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source)
at javafx.event.Event.fireEvent(Unknown Source)
at javafx.scene.control.MenuItem.fire(Unknown Source)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(Unknown Source)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.lambda$createChildren1(Unknown Source)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer$$Lambda3/1964728071.handle(Unknown Source)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(Unknown Source)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source)
at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source)
at javafx.event.Event.fireEvent(Unknown Source)
at javafx.scene.Scene$MouseHandler.process(Unknown Source)
at javafx.scene.Scene$MouseHandler.access00(Unknown Source)
at javafx.scene.Scene.impl_processMouseEvent(Unknown Source)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Unknown Source)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent0(Unknown Source)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$$Lambda2/1128114225.get(Unknown Source)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(Unknown Source)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(Unknown Source)
at com.sun.glass.ui.View.handleMouseEvent(Unknown Source)
at com.sun.glass.ui.View.notifyMouse(Unknown Source)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null5(Unknown Source)
at com.sun.glass.ui.win.WinApplication$$Lambda/2117255219.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NullPointerException
at application.MyController.updateListBuffers(MyController.java:399)
at application.MyController.initialize(MyController.java:114)
... 65 more
你的问题几乎不可能回答,因为你的代码比重现问题所需的代码多得多,但你没有包含关键元素(例如 FXML 文件)。最好从头开始创建一个小的、简单的、完整的示例来演示问题,而不是发布您的控制器的全部内容 classes.
以下是一些可能适用于您遇到的问题的一般情况。
下面是调用 FXMLLoader
的 load(...)
方法时发生的基本过程:
FXMLLoader
加载 FXML 文件。
- 如果根元素有
fx:controller
属性,并且没有
之前已设置控制器,加载程序将字符串转换为 Class
。如果在加载程序上设置了 controllerFactory
,那么将检索构造函数作为将 Class
传递给控制器工厂的 call(...)
方法的结果。否则,将通过指定的 class' 无参数构造函数实例化控制器。 (如果之前设置了控制器,则加载过程会异常终止。)
- 加载器解析FXML 文件,创建指定的对象,并将它们传递给彼此的各种set 方法,这些方法由FXML 结构决定。当创建了一个具有
fx:id
属性的对象,并且(通过任何方式)创建了一个控制器时,控制器中的一个 @FXML
注释字段与 fx:id
值(如果存在)被初始化为该对象。
- 如果控制器已创建,并且它有一个
initialize()
方法,则调用 initialize()
方法。
- 加载方法通过返回对与根元素对应的对象的引用来完成。
从编码的角度来看,这里有一些后果:
声明和初始化一个 @FXML
-注释字段,例如:
@FXML
private Label myLabel = new Label();
总是多余的。如果fx:id
设置正确,那么myLabel
在加载过程中会被重新赋值,原来的初始化值会被丢弃。更糟糕的是,如果 fx:id
设置不正确,使用 myLabel
所做的任何操作都将应用于不属于场景图的标签,因此您将获得难以调试的意外结果(基本上,这些操作会成功,但在 UI 中没有可见的效果)。如果您不初始化该字段,那么如果 fx:id
设置不正确,它将立即失败并显示 NullPointerException
,然后您可以轻松调试。
同样,在 initialize()
方法中重新初始化一个 @FXML
注释字段总是一场灾难:
@FXML
private Label myLabel ;
public void initialize() {
myLabel = new Label(...);
// ...
}
在这种情况下,myLabel
首先由 FXML 加载程序初始化,但是当 initialize()
方法被调用时,它被一个不属于场景图的不同标签替换。因此,对 myLabel
的操作将成功,但对 UI.
没有明显影响
其次,在 "standard" 控制器设置中,您不在 FXMLLoader
上调用 setController
或 setControllerFactory
,而是指定控制器 class 通过 fx:controller
属性,每个 FXMLLoader
创建对应控制器 class 的一个实例。从您的堆栈跟踪来看,ApplicationWindow.fxml
和 CreateProjectWindow.fxml
似乎都使用相同的控制器 class。这总是一个坏主意:共享控制器 class 的每个实例只有在对应于特定 FXML 文件定义的 FXML 元素时才会初始化 @FXML
注释字段。显然他们不会知道彼此的数据。您应该为每个 FXML 文件创建一个不同的控制器 class。如果他们需要共享数据,请使用 this question 中概述的技术。通常,您可以通过在一个控制器中创建和公开一个可观察对象 属性,并从另一个控制器观察它来管理它,如下例所示。
这是一个简单的例子,我猜你的例子有一些相同的结构。这可能有助于研究和理解这一点。这里的所有内容都放在一个名为 "application" 的包中(包括 FXML 文件)。
数据模型class:Person.java
package application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
private final StringProperty firstName = new SimpleStringProperty();
private final StringProperty lastName = new SimpleStringProperty();
public Person(String firstName, String lastName) {
setFirstName(firstName);
setLastName(lastName);
}
public final StringProperty firstNameProperty() {
return this.firstName;
}
public final String getFirstName() {
return this.firstNameProperty().get();
}
public final void setFirstName(final String firstName) {
this.firstNameProperty().set(firstName);
}
public final StringProperty lastNameProperty() {
return this.lastName;
}
public final String getLastName() {
return this.lastNameProperty().get();
}
public final void setLastName(final String lastName) {
this.lastNameProperty().set(lastName);
}
@Override
public String toString() {
return getFirstName() +" "+getLastName();
}
}
主 fxml 文件:PersonList.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1"
fx:controller="application.PersonListController">
<center>
<ListView fx:id="personList" />
</center>
<bottom>
<HBox spacing="5">
<padding>
<Insets top="10" bottom="10" left="10" right="10" />
</padding>
<Button text="New..." onAction="#newPerson" />
<Button text="Edit..." onAction="#editPerson" fx:id="editButton" />
</HBox>
</bottom>
</BorderPane>
主控制器:PersonListController.java:
package application;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.stage.Stage;
public class PersonListController {
@FXML
private ListView<Person> personList ;
@FXML
private Button editButton ;
public void initialize() {
// list created so it fires updates if either the firstName or lastName change:
ObservableList<Person> people = FXCollections.observableArrayList(person ->
new Observable[] {person.firstNameProperty(), person.lastNameProperty()});
people.addAll(new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson"),
new Person("Ethan", "Williams"),
new Person("Emma", "Jones"),
new Person("Michael", "Johnson"));
personList.setItems(people);
editButton.disableProperty().bind(
Bindings.isNull(personList.getSelectionModel().selectedItemProperty()));
}
@FXML
private void newPerson() throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("PersonEditor.fxml"));
Parent root = loader.load();
PersonEditorController controller = loader.getController();
controller.personProperty().addListener((obs, oldPerson, newPerson) -> {
if (newPerson != null) {
personList.getItems().add(newPerson);
}
});
showEditorWindow(root);
}
@FXML
private void editPerson() throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("PersonEditor.fxml"));
Parent root = loader.load();
PersonEditorController controller = loader.getController();
controller.setPerson(personList.getSelectionModel().getSelectedItem());
showEditorWindow(root);
}
private void showEditorWindow(Parent root) {
Scene scene = new Scene(root, 350, 200);
Stage stage = new Stage();
stage.initOwner(personList.getScene().getWindow());
stage.setScene(scene);
stage.show();
}
}
编辑器 fxml:PersonEditor.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<GridPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.PersonEditorController">
<Label text="First Name:" GridPane.columnIndex="0"
GridPane.rowIndex="0" />
<Label text="Last Name:" GridPane.columnIndex="0"
GridPane.rowIndex="1" />
<TextField fx:id="firstNameTF" GridPane.columnIndex="1"
GridPane.rowIndex="0" />
<TextField fx:id="lastNameTF" GridPane.columnIndex="1"
GridPane.rowIndex="1" />
<HBox GridPane.columnIndex="0" GridPane.rowIndex="2"
GridPane.columnSpan="2" spacing="5">
<padding>
<Insets top="5" bottom="5" left="5" right="5" />
</padding>
<Button text="OK" onAction="#submit" />
<Button text="Cancel" onAction="#cancel" />
</HBox>
</GridPane>
编辑器控制器:PersonEditorController.java:
package application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
public class PersonEditorController {
@FXML
private TextField firstNameTF ;
@FXML
private TextField lastNameTF ;
private final ObjectProperty<Person> person = new SimpleObjectProperty<>();
public ObjectProperty<Person> personProperty() {
return person;
}
public final Person getPerson() {
return personProperty().get();
}
public final void setPerson(Person person) {
personProperty().set(person);
}
public void initialize() {
personProperty().addListener((obs, oldPerson, newPerson) -> {
if (newPerson != null) {
firstNameTF.setText(newPerson.getFirstName());
lastNameTF.setText(newPerson.getLastName());
}
});
}
@FXML
private void submit() {
if (person.get() == null) {
person.set(new Person(firstNameTF.getText(), lastNameTF.getText()));
} else {
person.get().setFirstName(firstNameTF.getText());
person.get().setLastName(lastNameTF.getText());
}
close();
}
@FXML
private void cancel() {
close();
}
private void close() {
firstNameTF.getScene().getWindow().hide();
}
}
申请:Main.java:
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.fxml.FXMLLoader;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("PersonList.fxml"));
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
所以我正在用JavaFX制作一个小程序来熟悉它。
我很确定我对 javafx 的工作原理有一些基本的了解,但我似乎无法理解,因为我遇到了一些我找不到解决方案的问题。尽管在 google.
上对它们进行了彻底研究目前我有一个使用 eclipse 和 scenebuilder 1.1 的设置(因为 2.0 给我带来了各种麻烦)。
我有一个非常简单的设置,主应用程序加载初始主应用程序 window。
public class Main extends Application {
@Override
public void start(final Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/application/ApplicationWindow.fxml"));
Scene scene = new Scene(root);
stage.getIcons().add(new Image("/application/bug3.png"));
stage.setResizable(true);
stage.setTitle("Simple Bugtracker");
stage.setMinHeight(500);
stage.setMinWidth(800);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) { launch(args); }
}
它似乎工作正常。然后我创建了一个控制器 class,它包含正确的 fx:id'et gui 元素作为字段值,它工作正常,我可以用 java 代码来操作它们。
不过到目前为止我遇到了两个问题。
1) 当我从我的一个列表视图中删除某些内容时,我设法解决了更新问题,因此它通过添加一个 if null 场景立即将其从列表视图中删除。但是当我创建我的对象类型的新实例时,列表视图直到我重新启动程序才更新?
2) 其次,我在控制器 class 中遇到了一个简单的 int 字段值问题,我想在即时调用的方法中使用它的当前值(在 scenebuilder 中的按钮上设置),但是它似乎总是坚持认为即使值设置正确,它仍然是启动期间实例化时的原始值。
这是我的相当大的控制器 class。
public class MyController implements Initializable {
// Logic related fields
public ProjectList<Project> projectList = new ProjectList<Project>();
public int currentProjectIndex = -1;
public Bug currentBug;
// Listview buffers
public ObservableList<Project> projectsListBuffer = FXCollections.observableArrayList();
public ObservableList<Bug> unsolvedListBuffer = FXCollections.observableArrayList();
public ObservableList<Bug> solvedListBuffer = FXCollections.observableArrayList();
// GUI related fields
@FXML public ListView<Project> projectsListView;
@FXML public ListView<Bug> unsolvedListView;
@FXML public ListView<Bug> solvedListView;
@FXML public TextArea topDisplayArea;
@FXML public Button btnCreateProject;
@FXML public TextField titleFieldCreateProject;
@FXML public TextArea descriptionAreaCreateProject;
@FXML public AnchorPane createProjectWindow;
@FXML public AnchorPane projectsListViewAnchor;
@FXML public Label projectTitleLabel;
@FXML public Button createBugButton;
@FXML public TextField titleFieldCreateBug;
@FXML public TextArea descriptionAreaCreateBug;
public Stage createProjectStage;
// public Stage createBugStage;
@Override
public void initialize(URL arg0, ResourceBundle arg1) {
// load data from file system
try {
loadData();
} catch (IOException e) {
e.printStackTrace();
}
// Instantiate gui field values
// projectsListView = new ListView();
// unsolvedListView = new ListView();
// solvedListView = new ListView();
//
//
// topDisplayArea = new TextArea();
// btnCreateProject = new Button();
//
// titleFieldCreateProject = new TextField();
// descriptionAreaCreateProject = new TextArea();
//
// createProjectWindow = new AnchorPane();
// projectsListViewAnchor = new AnchorPane();
//
// projectTitleLabel = new Label();
// createBugButton = new Button();
//
// titleFieldCreateBug = new TextField();
// descriptionAreaCreateBug = new TextArea();
updateListBuffers();
projectsListView.setPrefHeight( getScreenH() );
projectsListView.setItems(projectsListBuffer);
unsolvedListView.setPrefHeight( getScreenH() );
unsolvedListView.setItems(unsolvedListBuffer);
// settings on necessary gui items
topDisplayArea.setPrefHeight( getScreenH() );
topDisplayArea.setPrefWidth( getScreenW() );
topDisplayArea.setEditable(false);
topDisplayArea.setBackground(null); // something to be done here for transparent logo in background of all project descriptions etc.
// ved dobbeltklik i projektlisten, vælg project og sæt titel
projectsListView.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent click) {
if (click.getClickCount() == 2) {
//Use ListView's getSelected Item
currentProjectIndex = projectsListView.getSelectionModel().getSelectedIndex();
Project currentProject = projectList.get(projectsListView.getSelectionModel().getSelectedIndex());
projectTitleLabel.setText(currentProject.getTitle());
System.out.println( "Selected project:"+currentProject.getTitle());
solvedListView.setItems(unsolvedListBuffer);
for (Bug b : currentProject.solvedBugs) {
System.out.println(b.getTitle()+"\n"+b.getErrorDescription());
}
updateListBuffers();
}
}
});
// ved dobbeltklik i uløste bugs listen, sæt top display område
unsolvedListView.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent click) {
if (click.getClickCount() == 2) {
//Use ListView's getSelected Item
currentBug = unsolvedListView.getSelectionModel().getSelectedItem();
topDisplayArea.setText(currentBug.getErrorDescription());
}
}
});
solvedListView.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent click) {
if (click.getClickCount() == 2) {
//Use ListView's getSelected Item
currentBug = solvedListView.getSelectionModel().getSelectedItem();
topDisplayArea.setText(currentBug.getErrorDescription());
}
}
});
// custom listview cell for the projects listview
projectsListView.setCellFactory(new Callback<ListView<Project>, ListCell<Project>>() {
@Override
public ListCell<Project> call(ListView<Project> p) {
ListCell<Project> cell = new ListCell<Project>() {
@Override
protected void updateItem(Project t, boolean bln) {
super.updateItem(t, bln);
if (t != null) {
// program a custom cell with several items in it
Text text = new Text();
Text text2 = new Text();
text.wrappingWidthProperty().bind(p.widthProperty().subtract(15));
text.setText(t.getTitle());
text2.wrappingWidthProperty().bind(p.widthProperty().subtract(15));
text2.setText(t.getCreationDate().toLocaleString());
text.setFont(Font.font ("Verdana", 12));
text2.setFont(Font.font ("Verdana", 10));
text.setFill(Color.BLACK);
text2.setFill(Color.TEAL);
AnchorPane pane = new AnchorPane();
pane.setPrefHeight(90);
pane.setPrefWidth(300);
ImageView imageView = new ImageView();
imageView.setImage(new Image("/application/project1.png"));
imageView.setFitHeight(60);
imageView.setFitWidth(60);
Image imageOk = new Image(getClass().getResourceAsStream("/application/selectionicon.png"));
Button deleteButton = new Button("Delete");
deleteButton.setLayoutX(233);
deleteButton.setLayoutY(60);
text.setLayoutX(70);
text.setLayoutY(30);
text2.setLayoutX(70);
text2.setLayoutY(50);
pane.getChildren().addAll(imageView, text, text2, deleteButton);
// delete actionevent
deleteButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Confirm deletion");
alert.setHeaderText("Are you sure you want to delete this project?");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK){
int selIndex = projectsListView.getSelectionModel().getSelectedIndex();
if (selIndex>-1) {
projectsListBuffer.remove(selIndex);
projectList.remove(selIndex);
projectsListView.setItems(projectsListBuffer);
projectList.save();
}
} else {
System.out.println("DENIED!!");
}
}
});
setPrefWidth(0);
setGraphic(pane);
} else {
setText(null);
setGraphic(null);
}
}
};
return cell;
}
});
// custom listview cell for the unsolved bugs listview
unsolvedListView.setCellFactory(new Callback<ListView<Bug>, ListCell<Bug>>() {
@Override
public ListCell<Bug> call(ListView<Bug> p) {
ListCell<Bug> cell = new ListCell<Bug>() {
@Override
protected void updateItem(Bug t, boolean bln) {
super.updateItem(t, bln);
if (t != null) {
// program a custom cell with several items in it
Text text = new Text();
Text text2 = new Text();
text.wrappingWidthProperty().bind(p.widthProperty().subtract(15));
text.setText(t.getTitle());
text2.wrappingWidthProperty().bind(p.widthProperty().subtract(15));
text2.setText(t.getCreationDate().toLocaleString());
text.setFont(Font.font ("Verdana", 12));
text2.setFont(Font.font ("Verdana", 10));
text.setFill(Color.BLACK);
text2.setFill(Color.TEAL);
AnchorPane pane = new AnchorPane();
pane.setPrefHeight(90);
pane.setPrefWidth(300);
ImageView imageView = new ImageView();
imageView.setImage(new Image("/application/bug3.png"));
imageView.setFitHeight(60);
imageView.setFitWidth(60);
Image imageOk = new Image(getClass().getResourceAsStream("/application/selectionicon.png"));
Button selectButton = new Button("Select");
Button deleteButton = new Button("Delete");
selectButton.setLayoutX(233);
selectButton.setLayoutY(60);
deleteButton.setLayoutX(180);
deleteButton.setLayoutY(60);
text.setLayoutX(70);
text.setLayoutY(30);
text2.setLayoutX(70);
text2.setLayoutY(50);
pane.getChildren().addAll(imageView, text, text2);
deleteButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
projectsListBuffer.remove(projectsListView.getSelectionModel().getSelectedIndex());
projectList.remove(projectsListView.getSelectionModel().getSelectedIndex());
projectsListView.setItems(projectsListBuffer);
projectList.save();
}
});
setPrefWidth(0);
setGraphic(pane);
} else {
setText(null);
setGraphic(null);
}
}
};
return cell;
}
});
}
// loads and show the create project window from it's fxml file (CreateProjectWindow.fxml)
public void showCreateProjectWindow() {
try {
Parent root1 = FXMLLoader.load(getClass().getResource("/application/CreateProjectWindow.fxml"));
createProjectStage = new Stage();
Scene scene = new Scene(root1);
createProjectStage.setScene(scene);
createProjectStage.setMinWidth(374);
createProjectStage.setMinHeight(416);
createProjectStage.setResizable(false);
createProjectStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
// actionevent method that creates a new project MANGLER AT FIKSE DET SÅ LISTVIEWET OPDATERER SIG MED DET SAMME
public void createProject() {
String title = titleFieldCreateProject.getText();
String description = descriptionAreaCreateProject.getText();
Project project = new Project(title, description, new Date());
projectList.add(project);
projectsListBuffer.add(project);
projectList.save();
updateListBuffers();
projectsListView.setItems(projectsListBuffer);
}
// loads and show the create project window from it's fxml file (CreateProjectWindow.fxml)
public void showCreateBugWindow() {
try {
Parent root1 = FXMLLoader.load(getClass().getResource("/application/CreateBugWindow.fxml"));
createProjectStage = new Stage();
Scene scene = new Scene(root1);
createProjectStage.setScene(scene);
createProjectStage.setMinWidth(374);
createProjectStage.setMinHeight(416);
createProjectStage.setResizable(false);
createProjectStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
// actionevent method that creates a new bug MANGLER AT FIKSE DET SÅ LISTVIEWET OPDATERER SIG MED DET SAMME
public void createBug() {
if (currentProjectIndex >-1) {
String title = titleFieldCreateBug.getText();
String description = descriptionAreaCreateBug.getText();
System.out.println(title+"\n"+description);
Bug bug = new Bug(title, description, new Date());
projectList.get(currentProjectIndex).unsolvedBugs.add(bug);
unsolvedListBuffer.add(bug);
projectList.save();
updateListBuffers();
} else {
System.out.println("Failed creation.");
}
}
// load, clear and reload buffers
public void updateListBuffers() {
if (currentProjectIndex>-1) {
unsolvedListBuffer.clear();
for (int i=0; i<projectList.get(currentProjectIndex).unsolvedBugs.size(); i++) {
unsolvedListBuffer.add( projectList.get(currentProjectIndex).unsolvedBugs.get(i) );
}
solvedListBuffer.clear();
for (int i=0; i<projectList.get(currentProjectIndex).solvedBugs.size(); i++) {
solvedListBuffer.add( projectList.get(currentProjectIndex).solvedBugs.get(i) );
}
// unsolvedListView.setItems(null);
// solvedListView.setItems(null);
unsolvedListView.setItems(unsolvedListBuffer);
solvedListView.setItems(solvedListBuffer);
}
projectsListBuffer.clear();
for (int i=0; i<projectList.size(); i++) {
projectsListBuffer.add( projectList.get(i) );
}
// projectsListView.setItems(null);
projectsListView.setItems(projectsListBuffer);
}
// file system related methods
public void loadData() throws IOException {
File fil = new File("Project_Data.dat");
if ( !fil.exists() ) {
fil.createNewFile();
projectList = new ProjectList<Project>();
projectList.save();
} else {
projectList = ProjectList.load();
}
}
// practical methods
public double getScreenH() {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
return screenSize.getHeight();
}
public double getScreenW() {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
return screenSize.getWidth();
}
}
我遇到问题的值是 currentProjectIndex,它似乎正确保存了它的值,但是当 "createBug()" 方法是 运行 单击按钮时,它说它仍然-1(与初始化时一样)?
关于第一个问题,如您所见,我正在调用 window 来创建新项目或错误,然后 运行 调用 createProject() 或 createBug() 方法,通过点击一个按钮。但这就像我 运行 某些方法时未保存控制器 (selectedProjectIndex) 中的字段值?
总之我可以成功创建项目,将它们保存到文件中。但是在程序重新启动之前列表视图不会更新。
其次,我不能创建任何错误,因为出于某种原因,selectedProjectIndex 始终被视为 -1?
编辑:使用新建议更新了控制器 class。
当我尝试使用 showCreateProjectWindow() 或 showCreateBugWindow() 方法打开新 window 时的堆栈跟踪。
/C:/Users/Giuseppe/Dropbox/Java/BugTracker/bin/application/CreateProjectWindow.fxml
at javafx.fxml.FXMLLoader.constructLoadException(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.load(Unknown Source)
at application.MyController.showCreateProjectWindow(MyController.java:320)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.reflect.misc.Trampoline.invoke(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.reflect.misc.MethodUtil.invoke(Unknown Source)
at javafx.fxml.FXMLLoader$MethodHandler.invoke(Unknown Source)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(Unknown Source)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source)
at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source)
at javafx.event.Event.fireEvent(Unknown Source)
at javafx.scene.control.MenuItem.fire(Unknown Source)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(Unknown Source)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.lambda$createChildren1(Unknown Source)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer$$Lambda3/1964728071.handle(Unknown Source)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(Unknown Source)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source)
at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source)
at javafx.event.Event.fireEvent(Unknown Source)
at javafx.scene.Scene$MouseHandler.process(Unknown Source)
at javafx.scene.Scene$MouseHandler.access00(Unknown Source)
at javafx.scene.Scene.impl_processMouseEvent(Unknown Source)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Unknown Source)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent0(Unknown Source)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$$Lambda2/1128114225.get(Unknown Source)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(Unknown Source)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(Unknown Source)
at com.sun.glass.ui.View.handleMouseEvent(Unknown Source)
at com.sun.glass.ui.View.notifyMouse(Unknown Source)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null5(Unknown Source)
at com.sun.glass.ui.win.WinApplication$$Lambda/2117255219.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NullPointerException
at application.MyController.updateListBuffers(MyController.java:399)
at application.MyController.initialize(MyController.java:114)
... 65 more
你的问题几乎不可能回答,因为你的代码比重现问题所需的代码多得多,但你没有包含关键元素(例如 FXML 文件)。最好从头开始创建一个小的、简单的、完整的示例来演示问题,而不是发布您的控制器的全部内容 classes.
以下是一些可能适用于您遇到的问题的一般情况。
下面是调用 FXMLLoader
的 load(...)
方法时发生的基本过程:
FXMLLoader
加载 FXML 文件。- 如果根元素有
fx:controller
属性,并且没有 之前已设置控制器,加载程序将字符串转换为Class
。如果在加载程序上设置了controllerFactory
,那么将检索构造函数作为将Class
传递给控制器工厂的call(...)
方法的结果。否则,将通过指定的 class' 无参数构造函数实例化控制器。 (如果之前设置了控制器,则加载过程会异常终止。) - 加载器解析FXML 文件,创建指定的对象,并将它们传递给彼此的各种set 方法,这些方法由FXML 结构决定。当创建了一个具有
fx:id
属性的对象,并且(通过任何方式)创建了一个控制器时,控制器中的一个@FXML
注释字段与fx:id
值(如果存在)被初始化为该对象。 - 如果控制器已创建,并且它有一个
initialize()
方法,则调用initialize()
方法。 - 加载方法通过返回对与根元素对应的对象的引用来完成。
从编码的角度来看,这里有一些后果:
声明和初始化一个 @FXML
-注释字段,例如:
@FXML
private Label myLabel = new Label();
总是多余的。如果fx:id
设置正确,那么myLabel
在加载过程中会被重新赋值,原来的初始化值会被丢弃。更糟糕的是,如果 fx:id
设置不正确,使用 myLabel
所做的任何操作都将应用于不属于场景图的标签,因此您将获得难以调试的意外结果(基本上,这些操作会成功,但在 UI 中没有可见的效果)。如果您不初始化该字段,那么如果 fx:id
设置不正确,它将立即失败并显示 NullPointerException
,然后您可以轻松调试。
同样,在 initialize()
方法中重新初始化一个 @FXML
注释字段总是一场灾难:
@FXML
private Label myLabel ;
public void initialize() {
myLabel = new Label(...);
// ...
}
在这种情况下,myLabel
首先由 FXML 加载程序初始化,但是当 initialize()
方法被调用时,它被一个不属于场景图的不同标签替换。因此,对 myLabel
的操作将成功,但对 UI.
其次,在 "standard" 控制器设置中,您不在 FXMLLoader
上调用 setController
或 setControllerFactory
,而是指定控制器 class 通过 fx:controller
属性,每个 FXMLLoader
创建对应控制器 class 的一个实例。从您的堆栈跟踪来看,ApplicationWindow.fxml
和 CreateProjectWindow.fxml
似乎都使用相同的控制器 class。这总是一个坏主意:共享控制器 class 的每个实例只有在对应于特定 FXML 文件定义的 FXML 元素时才会初始化 @FXML
注释字段。显然他们不会知道彼此的数据。您应该为每个 FXML 文件创建一个不同的控制器 class。如果他们需要共享数据,请使用 this question 中概述的技术。通常,您可以通过在一个控制器中创建和公开一个可观察对象 属性,并从另一个控制器观察它来管理它,如下例所示。
这是一个简单的例子,我猜你的例子有一些相同的结构。这可能有助于研究和理解这一点。这里的所有内容都放在一个名为 "application" 的包中(包括 FXML 文件)。
数据模型class:Person.java
package application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
private final StringProperty firstName = new SimpleStringProperty();
private final StringProperty lastName = new SimpleStringProperty();
public Person(String firstName, String lastName) {
setFirstName(firstName);
setLastName(lastName);
}
public final StringProperty firstNameProperty() {
return this.firstName;
}
public final String getFirstName() {
return this.firstNameProperty().get();
}
public final void setFirstName(final String firstName) {
this.firstNameProperty().set(firstName);
}
public final StringProperty lastNameProperty() {
return this.lastName;
}
public final String getLastName() {
return this.lastNameProperty().get();
}
public final void setLastName(final String lastName) {
this.lastNameProperty().set(lastName);
}
@Override
public String toString() {
return getFirstName() +" "+getLastName();
}
}
主 fxml 文件:PersonList.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1"
fx:controller="application.PersonListController">
<center>
<ListView fx:id="personList" />
</center>
<bottom>
<HBox spacing="5">
<padding>
<Insets top="10" bottom="10" left="10" right="10" />
</padding>
<Button text="New..." onAction="#newPerson" />
<Button text="Edit..." onAction="#editPerson" fx:id="editButton" />
</HBox>
</bottom>
</BorderPane>
主控制器:PersonListController.java:
package application;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.stage.Stage;
public class PersonListController {
@FXML
private ListView<Person> personList ;
@FXML
private Button editButton ;
public void initialize() {
// list created so it fires updates if either the firstName or lastName change:
ObservableList<Person> people = FXCollections.observableArrayList(person ->
new Observable[] {person.firstNameProperty(), person.lastNameProperty()});
people.addAll(new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson"),
new Person("Ethan", "Williams"),
new Person("Emma", "Jones"),
new Person("Michael", "Johnson"));
personList.setItems(people);
editButton.disableProperty().bind(
Bindings.isNull(personList.getSelectionModel().selectedItemProperty()));
}
@FXML
private void newPerson() throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("PersonEditor.fxml"));
Parent root = loader.load();
PersonEditorController controller = loader.getController();
controller.personProperty().addListener((obs, oldPerson, newPerson) -> {
if (newPerson != null) {
personList.getItems().add(newPerson);
}
});
showEditorWindow(root);
}
@FXML
private void editPerson() throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("PersonEditor.fxml"));
Parent root = loader.load();
PersonEditorController controller = loader.getController();
controller.setPerson(personList.getSelectionModel().getSelectedItem());
showEditorWindow(root);
}
private void showEditorWindow(Parent root) {
Scene scene = new Scene(root, 350, 200);
Stage stage = new Stage();
stage.initOwner(personList.getScene().getWindow());
stage.setScene(scene);
stage.show();
}
}
编辑器 fxml:PersonEditor.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<GridPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.PersonEditorController">
<Label text="First Name:" GridPane.columnIndex="0"
GridPane.rowIndex="0" />
<Label text="Last Name:" GridPane.columnIndex="0"
GridPane.rowIndex="1" />
<TextField fx:id="firstNameTF" GridPane.columnIndex="1"
GridPane.rowIndex="0" />
<TextField fx:id="lastNameTF" GridPane.columnIndex="1"
GridPane.rowIndex="1" />
<HBox GridPane.columnIndex="0" GridPane.rowIndex="2"
GridPane.columnSpan="2" spacing="5">
<padding>
<Insets top="5" bottom="5" left="5" right="5" />
</padding>
<Button text="OK" onAction="#submit" />
<Button text="Cancel" onAction="#cancel" />
</HBox>
</GridPane>
编辑器控制器:PersonEditorController.java:
package application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
public class PersonEditorController {
@FXML
private TextField firstNameTF ;
@FXML
private TextField lastNameTF ;
private final ObjectProperty<Person> person = new SimpleObjectProperty<>();
public ObjectProperty<Person> personProperty() {
return person;
}
public final Person getPerson() {
return personProperty().get();
}
public final void setPerson(Person person) {
personProperty().set(person);
}
public void initialize() {
personProperty().addListener((obs, oldPerson, newPerson) -> {
if (newPerson != null) {
firstNameTF.setText(newPerson.getFirstName());
lastNameTF.setText(newPerson.getLastName());
}
});
}
@FXML
private void submit() {
if (person.get() == null) {
person.set(new Person(firstNameTF.getText(), lastNameTF.getText()));
} else {
person.get().setFirstName(firstNameTF.getText());
person.get().setLastName(lastNameTF.getText());
}
close();
}
@FXML
private void cancel() {
close();
}
private void close() {
firstNameTF.getScene().getWindow().hide();
}
}
申请:Main.java:
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.fxml.FXMLLoader;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("PersonList.fxml"));
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}