翻译自 Table View
在本章中,您将学习如何使用JavaFX应用程序中的表执行基本操作,例如添加表,使用数据填充表以及编辑表行。
JavaFX SDK API中的几个类旨在以表格形式表示数据。用于创建JavaFX应用程序表中的最重要的类是TableView
,TableColumn
和TableCell
。您可以通过实现数据模型和应用单元工厂来填充表。
表类提供了内置功能,可以对列中的数据进行排序,并在必要时调整列的大小。
图12-1显示了表示通讯簿中的联系信息的典型表。
图12-1表样本
例12-1中的代码片段创建了一个包含三列的空表,并将其添加到应用程序场景中。
例12-1添加表
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class TableViewSample extends Application {
private TableView table = new TableView();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(300);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn firstNameCol = new TableColumn("First Name");
TableColumn lastNameCol = new TableColumn("Last Name");
TableColumn emailCol = new TableColumn("Email");
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
}
通过实例化TableView
类来创建表控件。在示例12-1中,它被添加到VBox
布局容器中,但是,您可以将其直接添加到应用程序场景中。
示例12-1定义了三列,用于将以下信息存储在地址簿中:联系人的名字和姓氏以及电子邮件地址。列是使用TableColumn
类创建的。
该类的getColumns
方法TableView
将以前创建的列添加到表中。在应用程序中,您可以使用此方法动态添加和删除列。
编译和运行此应用程序将生成如图12-2所示的输出。
图12-2没有数据的表
您可以通过调用setVisible
方法来管理列的可见性。例如,如果应用程序的逻辑需要隐藏用户电子邮件地址,则可以按如下方式实现此任务:emailCol.setVisible(false)
。
当数据结构需要更复杂的表示时,您可以创建嵌套列。
例如,假设地址簿中的联系人有两个电子邮件帐户。然后,您需要两列来显示主电子邮件地址和辅助电子邮件地址。创建两个子列,然后调用该getColumns
方法,emailCol
如例12-2所示。
示例12-2创建嵌套列
TableColumn firstEmailCol = new TableColumn("Primary");
TableColumn secondEmailCol = new TableColumn("Secondary");
emailCol.getColumns().addAll(firstEmailCol, secondEmailCol);
将这些行添加到示例12-1并编译并运行应用程序代码后,该表将如图12-3所示。
图12-3带嵌套列的表
虽然该表已添加到应用程序中,但会显示标准标题“表中没有内容”,因为未定义任何数据。您可以使用该setPlaceholder
方法指定Node
要在空表中显示的对象,而不是显示此标题。
在JavaFX应用程序中创建表时,最佳实践是实现定义数据模型的类,并提供方法和字段以进一步使用表。例12-3创建了一个Person
类来定义地址簿中的数据。
示例12-3创建Person类
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
的firstName
,lastName
和email
串属性的设置是为了使一个特定的数据元素的引用。
另外,为每个数据元素提供get
和set
方法。因此,例如,该getFirstName
方法返回firstName
属性的值,并且该setFirstName
方法指定此属性的值。
在Person
类中概述数据模型时,您可以创建一个ObservableList
数组并定义您希望在表中显示的数据行数。例12-4中的代码片段实现了此任务。
示例12-4在可观察列表中定义表数据
final ObservableList data = FXCollections.observableArrayList(
new Person("Jacob", "Smith", "[email protected]"),
new Person("Isabella", "Johnson", "[email protected]"),
new Person("Ethan", "Williams", "[email protected]"),
new Person("Emma", "Jones", "[email protected]"),
new Person("Michael", "Brown", "[email protected]")
);
下一步是将数据与表列相关联。您可以通过为每个数据元素定义的属性执行此操作,如例12-5所示。
示例12-5将数据属性设置为列
firstNameCol.setCellValueFactory(
new PropertyValueFactory("firstName")
);
lastNameCol.setCellValueFactory(
new PropertyValueFactory("lastName")
);
emailCol.setCellValueFactory(
new PropertyValueFactory("email")
);
该setCellValueFactory
方法为每列指定单元工厂。的细胞工厂通过使用实现PropertyValueFactory
类,它使用firstName
,lastName
和email
所述表的列的特性作为对相应方法的引用Person
类。
定义数据模型,并添加数据并与列关联时,可以使用类的setItems
方法将数据添加到表中TableView
:table.setItems(data)
。
由于该ObservableList
对象可以跟踪对其元素的任何更改,因此TableView
只要数据发生更改,内容就会自动更新。
检查例12-6中显示的应用程序代码。
示例12-6创建表并向其中添加数据
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class TableViewSample extends Application {
private TableView table = new TableView();
private final ObservableList data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "[email protected]"),
new Person("Isabella", "Johnson", "[email protected]"),
new Person("Ethan", "Williams", "[email protected]"),
new Person("Emma", "Jones", "[email protected]"),
new Person("Michael", "Brown", "[email protected]")
);
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(450);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory("firstName"));
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory("lastName"));
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory("email"));
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
}
编译并运行此应用程序代码时,将显示如图12-4所示的表。
图12-4填充数据的表
图12-4中的表包含五行数据,到目前为止无法修改。
您可以使用文本字段在“名字”,“姓氏”和“电子邮件”列中输入新值。“ 文本字段”控件使您的应用程序可以接收来自用户的文本输入。例12-7创建了三个文本字段,定义了每个字段的提示文本,并创建了Add按钮。
示例12-7使用文本字段在表中输入新项
final TextField addFirstName = new TextField();
addFirstName.setPromptText("First Name");
addFirstName.setMaxWidth(firstNameCol.getPrefWidth());
final TextField addLastName = new TextField();
addLastName.setMaxWidth(lastNameCol.getPrefWidth());
addLastName.setPromptText("Last Name");
final TextField addEmail = new TextField();
addEmail.setMaxWidth(emailCol.getPrefWidth());
addEmail.setPromptText("Email");
final Button addButton = new Button("Add");
addButton.setOnAction(new EventHandler() {
@Override public void handle(ActionEvent e) {
data.add(new Person(
addFirstName.getText(),
addLastName.getText(),
addEmail.getText()
));
addFirstName.clear();
addLastName.clear();
addEmail.clear();
}
});
当用户单击“添加”按钮时,在文本字段中输入的值将包含在Person
构造函数中并添加到data
可观察列表中。因此,带有联系信息的新条目将显示在表格中。
检查例12-8中显示的应用程序代码。
示例12-8包含要输入新项的文本字段的表
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class FileChooserSample extends Application {
private TableView table = new TableView();
private final ObservableList data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "[email protected]"),
new Person("Isabella", "Johnson", "[email protected]"),
new Person("Ethan", "Williams", "[email protected]"),
new Person("Emma", "Jones", "[email protected]"),
new Person("Michael", "Brown", "[email protected]"));
final HBox hb = new HBox();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(450);
stage.setHeight(550);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory("firstName"));
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory("lastName"));
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory("email"));
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
final TextField addFirstName = new TextField();
addFirstName.setPromptText("First Name");
addFirstName.setMaxWidth(firstNameCol.getPrefWidth());
final TextField addLastName = new TextField();
addLastName.setMaxWidth(lastNameCol.getPrefWidth());
addLastName.setPromptText("Last Name");
final TextField addEmail = new TextField();
addEmail.setMaxWidth(emailCol.getPrefWidth());
addEmail.setPromptText("Email");
final Button addButton = new Button("Add");
addButton.setOnAction(new EventHandler() {
@Override
public void handle(ActionEvent e) {
data.add(new Person(
addFirstName.getText(),
addLastName.getText(),
addEmail.getText()));
addFirstName.clear();
addLastName.clear();
addEmail.clear();
}
});
hb.getChildren().addAll(addFirstName, addLastName, addEmail, addButton);
hb.setSpacing(3);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table, hb);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
}
此应用程序不提供任何过滤器来检查,例如,是否以错误的格式输入了电子邮件地址。您可以在开发自己的应用程序时提供此类功能。
当前实现也不检查以确定是否输入空值。如果未提供任何值,则单击“添加”按钮会在表中插入一个空行。
图12-5演示了用户如何添加新行数据。
图12-5将通讯录添加到通讯簿
图12-6显示了单击“添加”按钮后的表格。Emma White的联系方式现已显示在表格中。
图12-6新增条目
该TableView
级提供了内置的功能来在列中的数据进行排序。用户可以通过单击列标题来更改数据的顺序。第一次单击启用升序排序,第二次单击启用降序排序,第三次单击禁用排序。默认情况下,不应用排序。
用户可以对表中的多个列进行排序,并在排序操作中指定每个列的优先级。要对多个列进行排序,用户在单击要排序的每个列的标题时按Shift键。
在图12-7中,升序排序顺序应用于名字,而姓氏则按降序排序。请注意,第一列优先于第二列。
图12-7排序多列
作为应用程序开发人员,您可以通过应用该setSortType
方法为应用程序中的每个列设置排序首选项。您可以指定升序和降序类型。例如,使用以下代码行为emailCol列设置降序排序类型:emailCol.setSortType(TableColumn.SortType.DESCENDING);
。
您还可以通过TableColumn
在TableView.sortOrder
可观察列表中添加和删除实例来指定要排序的列。此列表中的列顺序表示排序优先级(例如,零项的优先级高于第一项)。
要禁止对数据进行排序,请setSortable(false)
在列上调用该方法。
将TableView
不仅呈现类的表格数据,但它也提供了一些功能来编辑它。使用此setEditable
方法可以编辑表格内容。
使用该setCellFactory
方法在TextFieldTableCell
类的帮助下将表格单元格重新实现为文本字段。该setOnEditCommit
方法处理编辑并将更新的值分配给相应的表格单元格。示例12-9显示了如何应用这些方法来处理名字,姓氏和电子邮件列中的单元格编辑。
示例12-9实现单元格编辑
firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
firstNameCol.setOnEditCommit(
new EventHandler>() {
@Override
public void handle(CellEditEvent t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setFirstName(t.getNewValue());
}
}
);
lastNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
lastNameCol.setOnEditCommit(
new EventHandler>() {
@Override
public void handle(CellEditEvent t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setLastName(t.getNewValue());
}
}
);
emailCol.setCellFactory(TextFieldTableCell.forTableColumn());
emailCol.setOnEditCommit(
new EventHandler>() {
@Override
public void handle(CellEditEvent t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setEmail(t.getNewValue());
}
}
);
示例12-10中显示的应用程序的完整代码。
示例12-10具有启用单元格编辑的TableViewSample
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class TableViewSample extends Application {
private TableView table = new TableView();
private final ObservableList data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "[email protected]"),
new Person("Isabella", "Johnson", "[email protected]"),
new Person("Ethan", "Williams", "[email protected]"),
new Person("Emma", "Jones", "[email protected]"),
new Person("Michael", "Brown", "[email protected]"));
final HBox hb = new HBox();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(450);
stage.setHeight(550);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory("firstName"));
firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
firstNameCol.setOnEditCommit(
new EventHandler>() {
@Override
public void handle(CellEditEvent t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setFirstName(t.getNewValue());
}
}
);
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory("lastName"));
lastNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
lastNameCol.setOnEditCommit(
new EventHandler>() {
@Override
public void handle(CellEditEvent t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setLastName(t.getNewValue());
}
}
);
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory("email"));
emailCol.setCellFactory(TextFieldTableCell.forTableColumn());
emailCol.setOnEditCommit(
new EventHandler>() {
@Override
public void handle(CellEditEvent t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setEmail(t.getNewValue());
}
}
);
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
final TextField addFirstName = new TextField();
addFirstName.setPromptText("First Name");
addFirstName.setMaxWidth(firstNameCol.getPrefWidth());
final TextField addLastName = new TextField();
addLastName.setMaxWidth(lastNameCol.getPrefWidth());
addLastName.setPromptText("Last Name");
final TextField addEmail = new TextField();
addEmail.setMaxWidth(emailCol.getPrefWidth());
addEmail.setPromptText("Email");
final Button addButton = new Button("Add");
addButton.setOnAction(new EventHandler() {
@Override
public void handle(ActionEvent e) {
data.add(new Person(
addFirstName.getText(),
addLastName.getText(),
addEmail.getText()));
addFirstName.clear();
addLastName.clear();
addEmail.clear();
}
});
hb.getChildren().addAll(addFirstName, addLastName, addEmail, addButton);
hb.setSpacing(3);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table, hb);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
}
在图12-8中,用户正在编辑Michael Brown的姓氏。要编辑表格单元格,用户在单元格中输入新值,然后按Enter键。在按下Enter键之前,不会修改单元格。此行为由TextField
类的实现决定。
图12-8编辑表格单元格
请注意,TextField
控件的默认实现要求用户按Enter键提交编辑。您可以重新定义TextField
行为以在焦点更改上提交编辑,这是预期的用户体验。尝试修改代码来实现这样的替代行为。
例12-11细胞编辑的替代解决方案
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TableViewSample extends Application {
private TableView table = new TableView();
private final ObservableList data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "[email protected]"),
new Person("Isabella", "Johnson", "[email protected]"),
new Person("Ethan", "Williams", "[email protected]"),
new Person("Emma", "Jones", "[email protected]"),
new Person("Michael", "Brown", "[email protected]"));
final HBox hb = new HBox();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(450);
stage.setHeight(550);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
Callback cellFactory =
new Callback() {
public TableCell call(TableColumn p) {
return new EditingCell();
}
};
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory("firstName"));
firstNameCol.setCellFactory(cellFactory);
firstNameCol.setOnEditCommit(
new EventHandler>() {
@Override
public void handle(CellEditEvent t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setFirstName(t.getNewValue());
}
}
);
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory("lastName"));
lastNameCol.setCellFactory(cellFactory);
lastNameCol.setOnEditCommit(
new EventHandler>() {
@Override
public void handle(CellEditEvent t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setLastName(t.getNewValue());
}
}
);
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory("email"));
emailCol.setCellFactory(cellFactory);
emailCol.setOnEditCommit(
new EventHandler>() {
@Override
public void handle(CellEditEvent t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setEmail(t.getNewValue());
}
}
);
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
final TextField addFirstName = new TextField();
addFirstName.setPromptText("First Name");
addFirstName.setMaxWidth(firstNameCol.getPrefWidth());
final TextField addLastName = new TextField();
addLastName.setMaxWidth(lastNameCol.getPrefWidth());
addLastName.setPromptText("Last Name");
final TextField addEmail = new TextField();
addEmail.setMaxWidth(emailCol.getPrefWidth());
addEmail.setPromptText("Email");
final Button addButton = new Button("Add");
addButton.setOnAction(new EventHandler() {
@Override
public void handle(ActionEvent e) {
data.add(new Person(
addFirstName.getText(),
addLastName.getText(),
addEmail.getText()));
addFirstName.clear();
addLastName.clear();
addEmail.clear();
}
});
hb.getChildren().addAll(addFirstName, addLastName, addEmail, addButton);
hb.setSpacing(3);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table, hb);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
class EditingCell extends TableCell {
private TextField textField;
public EditingCell() {
}
@Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
textField.focusedProperty().addListener(new ChangeListener(){
@Override
public void changed(ObservableValue extends Boolean> arg0,
Boolean arg1, Boolean arg2) {
if (!arg2) {
commitEdit(textField.getText());
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
}
请注意,随着TextFieldTableCell
实现的发展,这种方法在将来的版本中可能会变得多余,以提供更好的用户体验。
启动JavaFX SDK 2.2,您可以将Map
数据添加到表中。使用示例12-12中MapValueFactory
所示的类来显示表中学生ID的映射。
示例12-12将映射数据添加到表中
import java.util.HashMap;
import java.util.Map;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.MapValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.StringConverter;
public class TableViewSample extends Application {
public static final String Column1MapKey = "A";
public static final String Column2MapKey = "B";
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(300);
stage.setHeight(500);
final Label label = new Label("Student IDs");
label.setFont(new Font("Arial", 20));
TableColumn
的MapValueFactory
类实现Callback
接口,并且它是专门设计用于表的列的细胞工厂内使用。在示例12-12中,dataRow哈希映射在TableView
对象中显示单个行。该映射有两个String
键:Column1MapKey和Column2MapKey,用于映射第一列和第二列中的相应值。setCellValueFactory
调用表列的方法使用与特定键匹配的数据填充它们,以便第一列包含与“A”键对应的值,第二列包含与“B”键对应的值。
编译并运行此应用程序时,它会生成如图12-9所示的输出。
图12-9带有地图数据的TableVIew
相关的API文档
TableView
TableColumn
TableCell
TextField
TextFieldTableCell
MapValueFactory
Button