翻译自 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);}@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);}}
的firstName
,lastName
和email
串属性的设置是为了使一个特定的数据元素的引用。
另外,为每个数据元素提供get
和set
方法。因此,例如,该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
类,它使用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<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);
。
您还可以通过TableColumn
在TableView.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