翻译自  Table View

在本章中,您将学习如何使用JavaFX应用程序中的表执行基本操作,例如添加表,使用数据填充表以及编辑表行。

JavaFX SDK API中的几个类旨在以表格形式表示数据。用于创建JavaFX应用程序表中的最重要的类是TableViewTableColumnTableCell。您可以通过实现数据模型和应用单元工厂来填充表。

表类提供了内置功能,可以对列中的数据进行排序,并在必要时调整列的大小。

图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);}@Overridepublic 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);}}

firstNamelastNameemail串属性的设置是为了使一个特定的数据元素的引用。

另外,为每个数据元素提供getset方法。因此,例如,该getFirstName方法返回firstName属性的值,并且该setFirstName方法指定此属性的值。

Person类中概述数据模型时,您可以创建一个ObservableList数组并定义您希望在表中显示的数据行数。例12-4中的代码片段实现了此任务。

示例12-4在可观察列表中定义表数据

final ObservableList<Person> data = FXCollections.observableArrayList(new Person("Jacob", "Smith", "jacob.smith@example.com"),new Person("Isabella", "Johnson", "isabella.johnson@example.com"),new Person("Ethan", "Williams", "ethan.williams@example.com"),new Person("Emma", "Jones", "emma.jones@example.com"),new Person("Michael", "Brown", "michael.brown@example.com")
);

下一步是将数据与表列相关联。您可以通过为每个数据元素定义的属性执行此操作,如例12-5所示。

示例12-5将数据属性设置为列

firstNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("firstName")
);
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("lastName")
);
emailCol.setCellValueFactory(new PropertyValueFactory<Person,String>("email")
);

setCellValueFactory方法为每列指定单元工厂。的细胞工厂通过使用实现PropertyValueFactory类,它使用firstNamelastNameemail所述表的列的特性作为对相应方法的引用Person类。

定义数据模型,并添加数据并与列关联时,可以使用类的setItems方法将数据添加到表中TableViewtable.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<Person> table = new TableView<Person>();private final ObservableList<Person> data =FXCollections.observableArrayList(new Person("Jacob", "Smith", "jacob.smith@example.com"),new Person("Isabella", "Johnson", "isabella.johnson@example.com"),new Person("Ethan", "Williams", "ethan.williams@example.com"),new Person("Emma", "Jones", "emma.jones@example.com"),new Person("Michael", "Brown", "michael.brown@example.com"));public static void main(String[] args) {launch(args);}@Overridepublic 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<Person, String>("firstName"));TableColumn lastNameCol = new TableColumn("Last Name");lastNameCol.setMinWidth(100);lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));TableColumn emailCol = new TableColumn("Email");emailCol.setMinWidth(200);emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("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<ActionEvent>() {@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<Person> table = new TableView<Person>();private final ObservableList<Person> data =FXCollections.observableArrayList(new Person("Jacob", "Smith", "jacob.smith@example.com"),new Person("Isabella", "Johnson", "isabella.johnson@example.com"),new Person("Ethan", "Williams", "ethan.williams@example.com"),new Person("Emma", "Jones", "emma.jones@example.com"),new Person("Michael", "Brown", "michael.brown@example.com"));final HBox hb = new HBox();public static void main(String[] args) {launch(args);}@Overridepublic 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<Person, String>("firstName"));TableColumn lastNameCol = new TableColumn("Last Name");lastNameCol.setMinWidth(100);lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));TableColumn emailCol = new TableColumn("Email");emailCol.setMinWidth(200);emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("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<ActionEvent>() {@Overridepublic 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);

您还可以通过TableColumnTableView.sortOrder可观察列表中添加和删​​除实例来指定要排序的列。此列表中的列顺序表示排序优先级(例如,零项的优先级高于第一项)。

要禁止对数据进行排序,请setSortable(false)在列上调用该方法。

编辑表格中的数据

TableView不仅呈现类的表格数据,但它也提供了一些功能来编辑它。使用此setEditable方法可以编辑表格内容。

使用该setCellFactory方法在TextFieldTableCell类的帮助下将表格单元格重新实现为文本字段。该setOnEditCommit方法处理编辑并将更新的值分配给相应的表格单元格。示例12-9显示了如何应用这些方法来处理名字,姓氏和电子邮件列中的单元格编辑。

示例12-9实现单元格编辑

firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
firstNameCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {@Overridepublic void handle(CellEditEvent<Person, String> t) {((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setFirstName(t.getNewValue());}}
);lastNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
lastNameCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {@Overridepublic void handle(CellEditEvent<Person, String> t) {((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setLastName(t.getNewValue());}}
);emailCol.setCellFactory(TextFieldTableCell.forTableColumn());
emailCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {@Overridepublic void handle(CellEditEvent<Person, String> 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<Person> table = new TableView<Person>();private final ObservableList<Person> data =FXCollections.observableArrayList(new Person("Jacob", "Smith", "jacob.smith@example.com"),new Person("Isabella", "Johnson", "isabella.johnson@example.com"),new Person("Ethan", "Williams", "ethan.williams@example.com"),new Person("Emma", "Jones", "emma.jones@example.com"),new Person("Michael", "Brown", "michael.brown@example.com"));final HBox hb = new HBox();public static void main(String[] args) {launch(args);}@Overridepublic 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<Person, String>("firstName"));firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn());firstNameCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {@Overridepublic void handle(CellEditEvent<Person, String> 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<Person, String>("lastName"));lastNameCol.setCellFactory(TextFieldTableCell.forTableColumn());lastNameCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {@Overridepublic void handle(CellEditEvent<Person, String> t) {((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setLastName(t.getNewValue());}});TableColumn emailCol = new TableColumn("Email");emailCol.setMinWidth(200);emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));emailCol.setCellFactory(TextFieldTableCell.forTableColumn());emailCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {@Overridepublic void handle(CellEditEvent<Person, String> 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<ActionEvent>() {@Overridepublic 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<Person> table = new TableView<Person>();private final ObservableList<Person> data =FXCollections.observableArrayList(new Person("Jacob", "Smith", "jacob.smith@example.com"),new Person("Isabella", "Johnson", "isabella.johnson@example.com"),new Person("Ethan", "Williams", "ethan.williams@example.com"),new Person("Emma", "Jones", "emma.jones@example.com"),new Person("Michael", "Brown", "michael.brown@example.com"));final HBox hb = new HBox();public static void main(String[] args) {launch(args);}@Overridepublic 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<TableColumn, TableCell> cellFactory =new Callback<TableColumn, TableCell>() {public TableCell call(TableColumn p) {return new EditingCell();}};TableColumn firstNameCol = new TableColumn("First Name");firstNameCol.setMinWidth(100);firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));firstNameCol.setCellFactory(cellFactory);firstNameCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {@Overridepublic void handle(CellEditEvent<Person, String> 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<Person, String>("lastName"));lastNameCol.setCellFactory(cellFactory);lastNameCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {@Overridepublic void handle(CellEditEvent<Person, String> t) {((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setLastName(t.getNewValue());}});TableColumn emailCol = new TableColumn("Email");emailCol.setMinWidth(200);emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));emailCol.setCellFactory(cellFactory);emailCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {@Overridepublic void handle(CellEditEvent<Person, String> 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<ActionEvent>() {@Overridepublic 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<Person, String> {private TextField textField;public EditingCell() {}@Overridepublic void startEdit() {if (!isEmpty()) {super.startEdit();createTextField();setText(null);setGraphic(textField);textField.selectAll();}}@Overridepublic void cancelEdit() {super.cancelEdit();setText((String) getItem());setGraphic(null);}@Overridepublic 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<Boolean>(){@Overridepublic 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);}@Overridepublic 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<Map, String> firstDataColumn = new TableColumn<>("Class A");TableColumn<Map, String> secondDataColumn = new TableColumn<>("Class B");firstDataColumn.setCellValueFactory(new MapValueFactory(Column1MapKey));firstDataColumn.setMinWidth(130);secondDataColumn.setCellValueFactory(new MapValueFactory(Column2MapKey));secondDataColumn.setMinWidth(130);TableView table_view = new TableView<>(generateDataInMap());table_view.setEditable(true);table_view.getSelectionModel().setCellSelectionEnabled(true);table_view.getColumns().setAll(firstDataColumn, secondDataColumn);Callback<TableColumn<Map, String>, TableCell<Map, String>>cellFactoryForMap = new Callback<TableColumn<Map, String>,TableCell<Map, String>>() {@Overridepublic TableCell call(TableColumn p) {return new TextFieldTableCell(new StringConverter() {@Overridepublic String toString(Object t) {return t.toString();}@Overridepublic Object fromString(String string) {return string;}                                    });}};firstDataColumn.setCellFactory(cellFactoryForMap);secondDataColumn.setCellFactory(cellFactoryForMap);final VBox vbox = new VBox();vbox.setSpacing(5);vbox.setPadding(new Insets(10, 0, 0, 10));vbox.getChildren().addAll(label, table_view);((Group) scene.getRoot()).getChildren().addAll(vbox);stage.setScene(scene);stage.show();}private ObservableList<Map> generateDataInMap() {int max = 10;ObservableList<Map> allData = FXCollections.observableArrayList();for (int i = 1; i < max; i++) {Map<String, String> dataRow = new HashMap<>();String value1 = "A" + i;String value2 = "B" + i;dataRow.put(Column1MapKey, value1);dataRow.put(Column2MapKey, value2);allData.add(dataRow);}return allData;}
}

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

JavaFX UI控件教程(十三)之Table View相关推荐

  1. JavaFX UI控件教程(二)之JavaFX UI控件

    翻译自  JavaFX UI控件 本章概述了通过API提供的JavaFX UI控件. JavaFX UI控件是使用场景图中的节点构建的.因此,控件可以使用JavaFX平台的视觉丰富功能.由于JavaF ...

  2. JavaFX UI控件教程(一)之简述

    翻译自  JavaFX UI控件概述 关于本教程 本教程介绍JavaFX API中提供的内置JavaFX UI控件. 该文件包含以下章节: JavaFX UI控件 标签 按钮 单选按钮 切换按钮 复选 ...

  3. JavaFX UI控件教程(八)之Choice Box

    翻译自   Choice Box 本章介绍了选项框,这些UI控件提供了在几个选项之间快速选择的支持. 使用ChoiceBox该类将选择框添加到JavaFX应用程序.其简单的实现如图7-1所示. 图7- ...

  4. JavaFX UI控件教程(二十三)之Menu

    翻译自  Menu 本章介绍如何创建菜单和菜单栏,添加菜单项,将菜单分组,创建子菜单以及设置上下文菜单. 您可以使用以下JavaFX API类在JavaFX应用程序中构建菜单. 菜单栏 菜单项 菜单 ...

  5. JavaFX UI控件教程(二十八)之UI控件的自定义

    翻译自  Customization of UI Controls 本章介绍了UI控件自定义的各个方面,并总结了Oracle提供的一些提示和技巧,以帮助您修改UI控件的外观和行为. 您可以通过应用层叠 ...

  6. JavaFX UI控件教程(二十二)之Titled Pane和Accordion

    翻译自  Titled Pane and Accordion 本章介绍如何在JavaFX应用程序中使用accordion和title窗格的组合. 标题窗格是带标题的面板.它可以打开和关闭,它可以封装任 ...

  7. JavaFX UI控件教程(二十一)之Tooltip

    翻译自  Tooltip 在本章中,您将了解工具提示,即当鼠标光标悬停该控件时,可以为任何UI控件设置的控件. 的Tooltip类表示通常用于显示关于所述用户接口的控制附加信息的公共UI组件.可以通过 ...

  8. JavaFX UI控件教程(二十)之HTML Editor

    翻译自  HTML Editor 在本章中,您将学习如何使用嵌入式HTML编辑器编辑JavaFX应用程序中的文本. 该HTMLEditor控件是一个功能齐全的富文本编辑器.它的实现基于HTML5的文档 ...

  9. JavaFX UI控件教程(十八)之Progress Bar和Progress Indicator

    翻译自  Progress Bar and Progress Indicator 在本章中,您将了解进度指示器和进度条,以及可视化JavaFX应用程序中任何操作进度的UI控件. 本ProgressIn ...

  10. JavaFX UI控件教程(十六)之Separator

    翻译自  Separator 本章介绍如何使用分隔符组织JavaFX应用程序的UI组件. SeparatorJavaFX API中可用的类表示水平或垂直分隔线.它用于划分应用程序用户界面的元素,不会产 ...

最新文章

  1. 偏差是什么?一文读懂偏差
  2. python isalpha()
  3. 无线覆盖项目地勘——无线地勘记录
  4. java.lang.NoClassDefFoundError comfasterxmljacksonannotationJsonView
  5. MasterPage + UpdatePanel + FileUpload
  6. labelimg如何调整框的颜色_P图改字:来看看吧!教你如何用PS墙壁发光灯泡广告文字图片...
  7. 图神经网络(一)图信号处理与图卷积神经网络(2)图信号与图的拉普拉斯矩阵
  8. 用BusyBox制作Linux根文件系统
  9. mysql注入漏洞语句,web安全之sql注入漏洞
  10. java中session的作用_java中session的工作原理是什么?和Cookies有何区别?
  11. python每隔30s检查一次_Python的全局解释器锁
  12. 【英语学习】【WOTD】apposite 释义/词源/示例
  13. Android状态选择器用法总结
  14. java jax ws_Java 7是否包含JAX-WS实现或API?
  15. Axure RP安装破解汉化以及发布到至AxureShare
  16. Ubuntu 安装绿联CM448无线网卡驱动
  17. 单片机74LS164C语言例子,74ls164单片机编程汇总(跑马灯/驱动数码管)
  18. kd树的构造和搜索(超详细)
  19. 怎么设计计算机网络共享,怎么设置网络共享?教大家两种设置共享网络的方法...
  20. 南京梧桐树美吗?生活在梧桐树下的痛苦,春天掉毛、夏天掉皮、秋天掉叶,一年三掉,砸的你是一把鼻涕一把泪。

热门文章

  1. java并发之synchronized实现原理及其优化
  2. [Java基础]反射概述
  3. php 如何生成二级目录json,使用PHP根据已解码的JSON创建文件夹/文件结构
  4. css3是什么 ptml_CSS3
  5. mysql数据剪切到新表_6、MySQL核心DDL语句
  6. Java Socket编程详解
  7. 2019 ICPC Asia Nanchang Regional K.Tree 树上启发式合并 + 动态开点线段树
  8. CF1479B Painting the Array
  9. P4878 [USACO05DEC]Layout G
  10. 线性代数五之高斯消元——[SDOI2010]外星千足虫,[HNOI2013]游走,[HNOI2011]XOR和路径,[hdu 4035]Maze