JAVAFX:控制器类中的字段值为';无法正确保存/更新listview时出现问题?
所以我用JavaFX制作了一个小程序来熟悉它 我很确定,我对javafx的工作原理有一些基本的了解,但这似乎让我难以理解,因为我遇到了一些无法找到解决方案的问题。尽管在谷歌上对它们进行了彻底的研究 目前我有一个使用eclipse和SceneBuilder1.1的设置(因为2.0给了我各种各样的麻烦) 我有一个非常简单的设置,其中有一个主应用程序,加载初始主应用程序窗口JAVAFX:控制器类中的字段值为';无法正确保存/更新listview时出现问题?,java,listview,javafx,scenebuilder,Java,Listview,Javafx,Scenebuilder,所以我用JavaFX制作了一个小程序来熟悉它 我很确定,我对javafx的工作原理有一些基本的了解,但这似乎让我难以理解,因为我遇到了一些无法找到解决方案的问题。尽管在谷歌上对它们进行了彻底的研究 目前我有一个使用eclipse和SceneBuilder1.1的设置(因为2.0给了我各种各样的麻烦) 我有一个非常简单的设置,其中有一个主应用程序,加载初始主应用程序窗口 public class Main extends Application { @Override
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); }
}
它似乎工作得很好。然后,我创建了一个控制器类,它包含正确的fx:id'etgui元素作为字段值,运行良好,我可以用java代码操作它们
到目前为止,我有两个问题
1) 当我从我的一个listview中删除某些内容时,我已经设法修复了更新问题,因此它通过添加if null场景立即将其从listview中删除。但是当我创建一个对象类型的新实例时,listview在我重新启动程序之前不会更新
2) 其次,我在控制器类中遇到了一个简单的int字段值的问题,我想在im调用的方法中使用它的当前值(在scenebuilder的按钮上设置),但它似乎总是坚持认为,即使该值设置正确,它仍然是在启动期间实例化时的原始值
这是我相当大的控制器类
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();
}
}
您的问题几乎不可能像您所拥有的那样得到回答,因为您拥有的代码远远多于重现问题所需的代码,但您没有包含关键元素(如FXML文件)。最好从头开始创建一个小的、简单的、完整的示例来演示问题,而不是发布一个完整的控制器类 以下是一些适用于您所看到的问题的一般情况 下面是调用
fxmloader
的load(…)
方法时发生的基本过程:
fxmloader
加载FXML文件fx:controller
属性,并且没有
控制器之前已设置,加载程序将字符串转换为类。如果在加载器上设置了controllerFactory
,则将类
传递给控制器工厂的调用(…)
方法后,将检索构造函数。否则,将通过指定的类“无参数构造函数”实例化控制器。(如果之前已设置控制器,则加载过程将终止,并出现异常。)
fx:id
属性的对象,并且(通过任何方式)创建了一个控制器时,控制器中带有与fx:id
值同名的@FXML
注释字段(如果存在)将初始化为该对象initialize()
方法,则调用initialize()
方法@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中产生可见的效果
其次,在“标准”控制器设置中,您不在fxmloader
上调用setController
或setControllerFactory
,而是通过fx:controller
属性指定控制器类,每个fxmloader
创建相应控制器类的一个实例。从堆栈跟踪来看,它看起来像是ApplicationWindow.fxml
和CreateProjectWindow.fxml
使用相同的控制器类。这总是一个坏主意:当共享控制器类的每个实例与特定FXML文件定义的FXML元素相对应时,它们只会初始化带有@FXML
-注释的字段。显然,他们不会知道彼此的数据。您应该为每个FXML文件创建不同的控制器类。如果他们需要共享数据,请使用中概述的技术。通常,您可以通过在一个控制器中创建和公开一个可观察属性,并从另一个控制器中观察它来管理这一点,如下例所示
这里有一个简单的例子,我猜你的例子有一些相同的结构。这可能有助于研究和理解这一点。这里的所有内容都放在一个名为“application”的包中(包括FXML文件)
数据模型类别:Pers
@FXML
private Label myLabel ;
public void initialize() {
myLabel = new Label(...);
// ...
}
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();
}
}
<?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>
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();
}
}
<?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>
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();
}
}
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);
}
}