来自 ObservableArrayList 的内容被加载(可能)但未显示
Content from ObservableArrayList gets loaded (probably) but not shown
我创建了一个小程序,用于从数据库加载数据并将其显示在 JavaFX 应用程序的 TableView 元素中。
问题是当我启动应用程序时,我看到的都是空行。不显示不同行的数据。但我确信它们以某种方式得到了处理,因为显示的空行数与从数据库加载的行数相匹配。
我的 System.out.prints 调试也从数据库中打印出正确的数据,所以我确定问题不在 DBLoader class.
我从 this 指南中获取了大部分内容。到目前为止,我所做的一切都是受到教程前两部分的启发。
编辑:是的,我添加了 DatabaseOverviewControll.java 作为 DatabaseOverview.fxml 的控制器 class。我添加了 itemNumberColumn、descriptionColumn 和 priceColumn 作为它们各自 TableColumn 元素的 fx:id。
我的 PrototypeApp class 扩展了应用程序,并且是 运行nable class:
package prototype;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import prototype.util.DBLoader;
import javafx.collections.*;
import prototype.view.DatabaseOverviewController;
import prototype.model.Row;
public class PrototypeApp extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
private ObservableList<Row> databaseData = FXCollections
.observableArrayList();
/**
* The constructor. Creates an dbLoader object and loads the database data
* from that dbLoader into the ObservableArrayList databaseData;
*/
public PrototypeApp() {
//DBLoader dbLoader = new DBLoader();
// databaseData = dbLoader.getData(); //This is used by me, but for the
// sake of testability I added some mock data instead.
// Some mock data for Stack Overflow users.
databaseData.add(new Row("abc", "efg", "hij"));
databaseData.add(new Row("abc2", "efg2", "hij2"));
databaseData.add(new Row("abc3", "efg3", "hij3"));
databaseData.add(new Row("abc4", "efg4", "hij4"));
// For debugging.
for (Row row : databaseData) {
System.out.println(row.getItemNumber() + "\t" + row.getDescription() + "\t" + row.getPrice());
}
}
public ObservableList<Row> getDatabaseData() {
return databaseData;
}
@Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Prototype");
initRootLayout();
showDatabaseOverview();
}
/**
* Initializes the root layout.
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(PrototypeApp.class
.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Shows the database overview inside the root layout.
*/
public void showDatabaseOverview() {
try {
// Load database overview
FXMLLoader loader = new FXMLLoader();
loader.setLocation(PrototypeApp.class
.getResource("view/DatabaseOverview.fxml"));
AnchorPane databaseOverview = (AnchorPane) loader.load();
// Set the database overview into the center of root layout.
rootLayout.setCenter(databaseOverview);
// Give the controller access to the prototype app.
DatabaseOverviewController controller = loader.getController();
controller.setPrototypeApp(this);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Returns the main stage.
*
* @return
*/
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
这是我的 DatabaseOverviewController class:
package prototype.view;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import prototype.PrototypeApp;
import prototype.model.Row;
public class DatabaseOverviewController {
@FXML
private TableView<Row> databaseTable;
@FXML
private TableColumn<Row, String> itemNumberColumn;
@FXML
private TableColumn<Row, String> descriptionColumn;
@FXML
private TableColumn<Row, String> priceColumn;
@FXML
private Label itemNumberLabel;
@FXML
private Label descriptionLabel;
@FXML
private Label priceLabel;
// Reference to the prototype application
private PrototypeApp prototypeApp;
/**
* The constructor The constructor is called before the initialize() method.
*/
public DatabaseOverviewController() {
}
/**
* Initializes a controller class. This method is automatically called after
* the fxml file has been loaded.
*/
@FXML
private void intialize() {
// Initialize the database table with the three columns.
itemNumberColumn.setCellValueFactory(cellData -> cellData.getValue()
.itemNumberProperty());
descriptionColumn.setCellValueFactory(cellData -> cellData.getValue()
.descriptionProperty());
priceColumn.setCellValueFactory(cellData -> cellData.getValue()
.priceProperty());
}
/**
* A method called by the prototype application to give a reference back to
* itself.
*
* @param prototypeApp
*/
public void setPrototypeApp(PrototypeApp prototypeApp) {
this.prototypeApp = prototypeApp;
// Adds observable list data to the table
databaseTable.setItems(prototypeApp.getDatabaseData());
}
}
这是我的 class 行,代表 SQL table 中的一行:
package prototype.model;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/*
* Model class for a SQL row.
*
* @author Jonatan Stenbacka
*/
public class Row {
private final StringProperty itemNumber;
private final StringProperty description;
private final StringProperty price;
/**
* Default constructor
*/
public Row() {
this(null, null, null);
}
/**
* Constructor
* @param itemNumber
* @param description
* @param price
*/
public Row (String itemNumber, String description, String price) {
this.itemNumber = new SimpleStringProperty(itemNumber);
this.description = new SimpleStringProperty(description);
this.price = new SimpleStringProperty(price);
}
public String getItemNumber() {
return itemNumber.get();
}
public void setItemNumber(String itemNumber) {
this.itemNumber.set(itemNumber);
}
public StringProperty itemNumberProperty() {
return itemNumber;
}
public String getDescription() {
return description.get();
}
public void setDescription(String description) {
this.description.set(description);
}
public StringProperty descriptionProperty() {
return description;
}
public String getPrice() {
return price.get();
}
public void setPrice(String price) {
this.price.set(price);
}
public StringProperty priceProperty() {
return price;
}
}
编辑:这是我的 FXML 文件。如果您只添加一些模拟行数据,应该足以 运行 程序。
DatabaseOverview.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="prototype.view.DatabaseOverviewController">
<children>
<BorderPane layoutX="191.0" layoutY="58.0" prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<top>
<Label text="Tables" BorderPane.alignment="CENTER">
<font>
<Font size="24.0" />
</font>
</Label>
</top>
<bottom>
<Button mnemonicParsing="false" onAction="#handleLoadDatabase" text="Load" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets bottom="10.0" />
</BorderPane.margin>
</Button>
</bottom>
<center>
<TableView fx:id="databaseTable" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<columns>
<TableColumn fx:id="itemNumberColumn" prefWidth="200.0" text="Item Number" />
<TableColumn fx:id="descriptionColumn" prefWidth="200.0" text="Description" />
<TableColumn fx:id="priceColumn" prefWidth="200.0" text="Price" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
</center>
</BorderPane>
</children>
</AnchorPane>
RootLayout.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40">
<top>
<MenuBar BorderPane.alignment="CENTER">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" text="Close" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Edit">
<items>
<MenuItem mnemonicParsing="false" text="Delete" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
</top>
</BorderPane>
DatabaseOverviewController
中有一个简单的拼写错误:您将 intialize()
作为方法名称,而不是 initialize()
。由于 FXMLLoader
使用反射来搜索名为 initialize
的方法,如果找不到则什么也不做,因此您的方法将静默地调用失败。更正拼写错误即可解决问题。
关于它的价值,以下是我的计算方法,因为它真的很难看出来。正如您所做的那样,我观察到 table 中存在正确的行数(您可以 select 它们),但没有显示任何内容。这几乎总是通过未设置 cellValueFactory
来调用(或设置不正确,如果您使用 PropertyValueFactory
可能会发生这种情况,但当您使用此处显示的 lambda 的好方法时很少发生) .
由于 initialize()
方法似乎正确地设置了这些,我首先添加了日志记录到 Row.itemNumberProperty()
,然后当我观察到该方法没有被调用时(它应该被调用该列中的每个 TableCell
)到 initialize()
方法本身。当我观察到 initialize()
方法没有被调用时,我发现了拼写错误。
不确定从这里发出的一般消息是什么(如果有的话)?或许最好使用让控制器实现 Initializable
的旧(JavaFX 2.1 之前)风格的方法,这样编译器将捕获错误键入的方法名称。我想如果要调用的 post-FXML 加载方法有一个特定的注释(@PostLoading
或其他)会更好。
我创建了一个小程序,用于从数据库加载数据并将其显示在 JavaFX 应用程序的 TableView 元素中。
问题是当我启动应用程序时,我看到的都是空行。不显示不同行的数据。但我确信它们以某种方式得到了处理,因为显示的空行数与从数据库加载的行数相匹配。
我的 System.out.prints 调试也从数据库中打印出正确的数据,所以我确定问题不在 DBLoader class.
我从 this 指南中获取了大部分内容。到目前为止,我所做的一切都是受到教程前两部分的启发。
编辑:是的,我添加了 DatabaseOverviewControll.java 作为 DatabaseOverview.fxml 的控制器 class。我添加了 itemNumberColumn、descriptionColumn 和 priceColumn 作为它们各自 TableColumn 元素的 fx:id。
我的 PrototypeApp class 扩展了应用程序,并且是 运行nable class:
package prototype;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import prototype.util.DBLoader;
import javafx.collections.*;
import prototype.view.DatabaseOverviewController;
import prototype.model.Row;
public class PrototypeApp extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
private ObservableList<Row> databaseData = FXCollections
.observableArrayList();
/**
* The constructor. Creates an dbLoader object and loads the database data
* from that dbLoader into the ObservableArrayList databaseData;
*/
public PrototypeApp() {
//DBLoader dbLoader = new DBLoader();
// databaseData = dbLoader.getData(); //This is used by me, but for the
// sake of testability I added some mock data instead.
// Some mock data for Stack Overflow users.
databaseData.add(new Row("abc", "efg", "hij"));
databaseData.add(new Row("abc2", "efg2", "hij2"));
databaseData.add(new Row("abc3", "efg3", "hij3"));
databaseData.add(new Row("abc4", "efg4", "hij4"));
// For debugging.
for (Row row : databaseData) {
System.out.println(row.getItemNumber() + "\t" + row.getDescription() + "\t" + row.getPrice());
}
}
public ObservableList<Row> getDatabaseData() {
return databaseData;
}
@Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Prototype");
initRootLayout();
showDatabaseOverview();
}
/**
* Initializes the root layout.
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(PrototypeApp.class
.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Shows the database overview inside the root layout.
*/
public void showDatabaseOverview() {
try {
// Load database overview
FXMLLoader loader = new FXMLLoader();
loader.setLocation(PrototypeApp.class
.getResource("view/DatabaseOverview.fxml"));
AnchorPane databaseOverview = (AnchorPane) loader.load();
// Set the database overview into the center of root layout.
rootLayout.setCenter(databaseOverview);
// Give the controller access to the prototype app.
DatabaseOverviewController controller = loader.getController();
controller.setPrototypeApp(this);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Returns the main stage.
*
* @return
*/
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
这是我的 DatabaseOverviewController class:
package prototype.view;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import prototype.PrototypeApp;
import prototype.model.Row;
public class DatabaseOverviewController {
@FXML
private TableView<Row> databaseTable;
@FXML
private TableColumn<Row, String> itemNumberColumn;
@FXML
private TableColumn<Row, String> descriptionColumn;
@FXML
private TableColumn<Row, String> priceColumn;
@FXML
private Label itemNumberLabel;
@FXML
private Label descriptionLabel;
@FXML
private Label priceLabel;
// Reference to the prototype application
private PrototypeApp prototypeApp;
/**
* The constructor The constructor is called before the initialize() method.
*/
public DatabaseOverviewController() {
}
/**
* Initializes a controller class. This method is automatically called after
* the fxml file has been loaded.
*/
@FXML
private void intialize() {
// Initialize the database table with the three columns.
itemNumberColumn.setCellValueFactory(cellData -> cellData.getValue()
.itemNumberProperty());
descriptionColumn.setCellValueFactory(cellData -> cellData.getValue()
.descriptionProperty());
priceColumn.setCellValueFactory(cellData -> cellData.getValue()
.priceProperty());
}
/**
* A method called by the prototype application to give a reference back to
* itself.
*
* @param prototypeApp
*/
public void setPrototypeApp(PrototypeApp prototypeApp) {
this.prototypeApp = prototypeApp;
// Adds observable list data to the table
databaseTable.setItems(prototypeApp.getDatabaseData());
}
}
这是我的 class 行,代表 SQL table 中的一行:
package prototype.model;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/*
* Model class for a SQL row.
*
* @author Jonatan Stenbacka
*/
public class Row {
private final StringProperty itemNumber;
private final StringProperty description;
private final StringProperty price;
/**
* Default constructor
*/
public Row() {
this(null, null, null);
}
/**
* Constructor
* @param itemNumber
* @param description
* @param price
*/
public Row (String itemNumber, String description, String price) {
this.itemNumber = new SimpleStringProperty(itemNumber);
this.description = new SimpleStringProperty(description);
this.price = new SimpleStringProperty(price);
}
public String getItemNumber() {
return itemNumber.get();
}
public void setItemNumber(String itemNumber) {
this.itemNumber.set(itemNumber);
}
public StringProperty itemNumberProperty() {
return itemNumber;
}
public String getDescription() {
return description.get();
}
public void setDescription(String description) {
this.description.set(description);
}
public StringProperty descriptionProperty() {
return description;
}
public String getPrice() {
return price.get();
}
public void setPrice(String price) {
this.price.set(price);
}
public StringProperty priceProperty() {
return price;
}
}
编辑:这是我的 FXML 文件。如果您只添加一些模拟行数据,应该足以 运行 程序。
DatabaseOverview.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="prototype.view.DatabaseOverviewController">
<children>
<BorderPane layoutX="191.0" layoutY="58.0" prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<top>
<Label text="Tables" BorderPane.alignment="CENTER">
<font>
<Font size="24.0" />
</font>
</Label>
</top>
<bottom>
<Button mnemonicParsing="false" onAction="#handleLoadDatabase" text="Load" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets bottom="10.0" />
</BorderPane.margin>
</Button>
</bottom>
<center>
<TableView fx:id="databaseTable" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<columns>
<TableColumn fx:id="itemNumberColumn" prefWidth="200.0" text="Item Number" />
<TableColumn fx:id="descriptionColumn" prefWidth="200.0" text="Description" />
<TableColumn fx:id="priceColumn" prefWidth="200.0" text="Price" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
</center>
</BorderPane>
</children>
</AnchorPane>
RootLayout.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40">
<top>
<MenuBar BorderPane.alignment="CENTER">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" text="Close" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Edit">
<items>
<MenuItem mnemonicParsing="false" text="Delete" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
</top>
</BorderPane>
DatabaseOverviewController
中有一个简单的拼写错误:您将 intialize()
作为方法名称,而不是 initialize()
。由于 FXMLLoader
使用反射来搜索名为 initialize
的方法,如果找不到则什么也不做,因此您的方法将静默地调用失败。更正拼写错误即可解决问题。
关于它的价值,以下是我的计算方法,因为它真的很难看出来。正如您所做的那样,我观察到 table 中存在正确的行数(您可以 select 它们),但没有显示任何内容。这几乎总是通过未设置 cellValueFactory
来调用(或设置不正确,如果您使用 PropertyValueFactory
可能会发生这种情况,但当您使用此处显示的 lambda 的好方法时很少发生) .
由于 initialize()
方法似乎正确地设置了这些,我首先添加了日志记录到 Row.itemNumberProperty()
,然后当我观察到该方法没有被调用时(它应该被调用该列中的每个 TableCell
)到 initialize()
方法本身。当我观察到 initialize()
方法没有被调用时,我发现了拼写错误。
不确定从这里发出的一般消息是什么(如果有的话)?或许最好使用让控制器实现 Initializable
的旧(JavaFX 2.1 之前)风格的方法,这样编译器将捕获错误键入的方法名称。我想如果要调用的 post-FXML 加载方法有一个特定的注释(@PostLoading
或其他)会更好。