JavaFX教程 - JavaFX表视图
我们可以使用来自 JavaFX API
的 TableView
,TableColumn
和 TableCell
类以表格形式表示数据。
通过实现数据模型和应用单元工厂来填充表中的数据。
表类可以按列排序数据,并在必要时调整列大小。
创建表
表控件是通过实例化 TableView
类创建的。
TableView table = new TableView();
table.setEditable(true);
然后使用 TableColumn
类创建三个列。TableView
类的 getColumns
方法将创建的列添加到表中。
TableColumn firstNameCol = new TableColumn("First Name");
TableColumn lastNameCol = new TableColumn("Last Name");
TableColumn emailCol = new TableColumn("Email");table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
我们可以通过调用 setVisible
方法隐藏列。
aColumn.setVisible(false).
以下代码创建一个表。
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;public class Main extends Application {public static void main(String[] args) {launch(args);}@Overridepublic void start(Stage primaryStage) {TableView table = new TableView();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);emailCol.setVisible(false);StackPane root = new StackPane();root.getChildren().add(table);primaryStage.setScene(new Scene(root, 200, 250));primaryStage.show();}
}
上面的代码生成以下结果。
嵌套标头
使用 JavaFX
表视图,我们可以轻松创建嵌套列。
假设我们要将两个子列添加到地址列。
TableColumn cityCol = new TableColumn("City");
TableColumn stateCol = new TableColumn("State");
然后我们将新创建的列添加到地址列。
addressCol.getColumns().addAll(cityCol, stateCol);
完整的源代码
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;public class Main extends Application {public static void main(String[] args) {launch(args);}@Overridepublic void start(Stage primaryStage) {TableView table = new TableView();table.setEditable(true);TableColumn firstNameCol = new TableColumn("First Name");TableColumn lastNameCol = new TableColumn("Last Name");TableColumn addressCol = new TableColumn("Email");table.getColumns().addAll(firstNameCol, lastNameCol, addressCol);TableColumn cityCol = new TableColumn("City");TableColumn stateCol = new TableColumn("State");addressCol.getColumns().addAll(cityCol, stateCol);StackPane root = new StackPane();root.getChildren().add(table);primaryStage.setScene(new Scene(root, 200, 250));primaryStage.show();}
}
上面的代码生成以下结果。
添加新行
以下代码显示如何向表视图中添加数据。 创建 JavaFX JavaBean
以保存单个行的值。 表中的每一行代表一个名字为姓氏的人。 JavaFX JavaBean
称为 Person
,它有两个字段,名字和姓氏。 Person
为这两个值提供了可绑定的属性。
在 UI
逻辑中,它使用 ObservableList
来保存表视图的值。 ObservableList
中的每个元素都是一个Person
对象。
在按钮事件处理程序中,它创建一个新的具有硬编码的名字和姓氏的人,然后添加到 ObservableList
。
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;public class Main extends Application {private final TableView<Person> table = new TableView<>();private final ObservableList<Person> data =FXCollections.observableArrayList(new Person("A", "B"));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.setWidth(450);stage.setHeight(550);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"));table.setItems(data);table.getColumns().addAll(firstNameCol, lastNameCol);final Button addButton = new Button("Add");addButton.setOnAction((ActionEvent e) -> {data.add(new Person("Z","X"));});hb.getChildren().addAll(addButton);hb.setSpacing(3);final VBox vbox = new VBox();vbox.setSpacing(5);vbox.setPadding(new Insets(10, 0, 0, 10));vbox.getChildren().addAll(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 Person(String fName, String lName) {this.firstName = new SimpleStringProperty(fName);this.lastName = new SimpleStringProperty(lName);}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);}}
}
上面的代码生成以下结果。
TableView
列排序
TableView
类具有列的内置排序功能。
我们可以通过点击列标题对数据进行排序。第一次单击启用升序排序,第二次点击使得排序顺序降序,第三个点击禁用排序。
默认情况下,不应用排序。
我们可以排序多个列,并在排序操作中指定每个列的优先级。
要对多个列进行排序,请在按住Shift
键的同时单击列标题。
我们可以使用 setSortType
方法设置排序首选项我们可以指定升序或降序类型。
以下代码设置排序的降序类型
aColumn.setSortType(TableColumn.SortType.DESCENDING);
我们可以通过从 TableView.sortOrder observable
列表中添加和删除 TableColumn
实例来指定要排序的列。
TableView.sortOrder observable
列中的列的顺序表示排序优先级。 列表中零索引的项目的优先级高于列表中的第一个项目。
要禁止对列进行排序,请调用该列上的 setSortable(false)
方法。
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;public class Main extends Application {private final TableView<Person> table = new TableView<>();private final ObservableList<Person> data =FXCollections.observableArrayList(new Person("A", "B"),new Person("D", "E"));public static void main(String[] args) {launch(args);}@Overridepublic void start(Stage stage) {Scene scene = new Scene(new Group());stage.setWidth(450);stage.setHeight(550);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"));table.setItems(data);table.getColumns().addAll(firstNameCol, lastNameCol);firstNameCol.setSortType(TableColumn.SortType.DESCENDING);lastNameCol.setSortable(false);final VBox vbox = new VBox();vbox.setSpacing(5);vbox.setPadding(new Insets(10, 0, 0, 10));vbox.getChildren().addAll(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 Person(String fName, String lName) {this.firstName = new SimpleStringProperty(fName);this.lastName = new SimpleStringProperty(lName);}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);}}
}
上面的代码生成以下结果。
编辑表中的数据
我们可以在表视图中编辑数据。通过调用 setEditable
方法,我们可以启用表内容的编辑。
setCellFactory
方法安装在 TextFieldTableCell
类中定义的文本字段为每个表单元格。
setOnEditCommit
方法更新表单元格,并且数据绑定需要考虑设置数据回到 JavaFX JavaBean
,它用作表视图的下划线数据模型。
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.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;public class Main extends Application {private final TableView<Person> table = new TableView<>();private final ObservableList<Person> data =FXCollections.observableArrayList(new Person("A", "B"),new Person("C", "D"));public static void main(String[] args) {launch(args);}@Overridepublic void start(Stage stage) {Scene scene = new Scene(new Group());stage.setWidth(450);stage.setHeight(550);table.setEditable(true);TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");firstNameCol.setMinWidth(100);firstNameCol.setCellValueFactory(new PropertyValueFactory<>("firstName"));firstNameCol.setCellFactory(TextFieldTableCell.<Person>forTableColumn());firstNameCol.setOnEditCommit((CellEditEvent<Person, String> t) -> {((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setFirstName(t.getNewValue());});TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");lastNameCol.setMinWidth(100);lastNameCol.setCellValueFactory(new PropertyValueFactory<>("lastName"));lastNameCol.setCellFactory(TextFieldTableCell.<Person>forTableColumn());lastNameCol.setOnEditCommit((CellEditEvent<Person, String> t) -> {((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setLastName(t.getNewValue());});table.setItems(data);table.getColumns().addAll(firstNameCol, lastNameCol);final VBox vbox = new VBox();vbox.setSpacing(5);vbox.setPadding(new Insets(10, 0, 0, 10));vbox.getChildren().addAll( 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 Person(String fName, String lName) {this.firstName = new SimpleStringProperty(fName);this.lastName = new SimpleStringProperty(lName);}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);}}
}
上面的代码生成以下结果。
表编辑提交事件
当运行上面的代码并双击编辑单元格,我们必须按 Enter
键提交编辑。 如果我们只是双击并键入一些值,并单击单元格外,commit
事件不会运行,旧的值将被填充。
我们可以重新定义焦点更改上的 commit
事件。
/** Copyright (c) 2008, 2014, Oracle and/or its affiliates.* All rights reserved. Use is subject to license terms.** This file is available and licensed under the following license:** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions* are met:** - Redistributions of source code must retain the above copyright* notice, this list of conditions and the following disclaimer.* - Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in* the documentation and/or other materials provided with the distribution.* - Neither the name of Oracle nor the names of its* contributors may be used to endorse or promote products derived* from this software without specific prior written permission.** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
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.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;public class Main extends Application {private final TableView<Person> table = new TableView<>();private final ObservableList<Person> data = FXCollections.observableArrayList(new Person("A", "B"), new Person("C", "D"));public static void main(String[] args) {launch(args);}@Overridepublic void start(Stage stage) {Callback<TableColumn<Person, String>, TableCell<Person, String>> cellFactory = (TableColumn<Person, String> p) -> new EditingCell();Scene scene = new Scene(new Group());stage.setWidth(450);stage.setHeight(550);table.setEditable(true);TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");firstNameCol.setMinWidth(100);firstNameCol.setCellValueFactory(new PropertyValueFactory<>("firstName"));firstNameCol.setCellFactory(cellFactory);firstNameCol.setOnEditCommit((CellEditEvent<Person, String> t) -> {((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setFirstName(t.getNewValue());});TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");lastNameCol.setMinWidth(100);lastNameCol.setCellValueFactory(new PropertyValueFactory<>("lastName"));table.setItems(data);table.getColumns().addAll(firstNameCol, lastNameCol);final VBox vbox = new VBox();vbox.setSpacing(5);vbox.setPadding(new Insets(10, 0, 0, 10));vbox.getChildren().addAll(table);((Group) scene.getRoot()).getChildren().addAll(vbox);stage.setScene(scene);stage.show();}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((ObservableValue<? extends Boolean> arg0, Boolean arg1,Boolean arg2) -> {if (!arg2) {commitEdit(textField.getText());}});}private String getString() {return getItem() == null ? "" : getItem().toString();}}public static class Person {private final SimpleStringProperty firstName;private final SimpleStringProperty lastName;private Person(String fName, String lName) {this.firstName = new SimpleStringProperty(fName);this.lastName = new SimpleStringProperty(lName);}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);}}
}
上面的代码生成以下结果。
将数据映射添加到表中
我们可以使用 MapValueFactory
类将 Map
数据添加到表中。
/** Copyright (c) 2008, 2014, Oracle and/or its affiliates.* All rights reserved. Use is subject to license terms.** This file is available and licensed under the following license:** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions* are met:** - Redistributions of source code must retain the above copyright* notice, this list of conditions and the following disclaimer.* - Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in* the documentation and/or other materials provided with the distribution.* - Neither the name of Oracle nor the names of its* contributors may be used to endorse or promote products derived* from this software without specific prior written permission.** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/
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 Main 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 tableView = new TableView<>(generateDataInMap());tableView.setEditable(true);tableView.getSelectionModel().setCellSelectionEnabled(true);tableView.getColumns().setAll(firstDataColumn, secondDataColumn);Callback<TableColumn<Map, String>, TableCell<Map, String>>cellFactoryForMap = (TableColumn<Map, String> p) -> 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, tableView);((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;}
}
上面的代码生成以下结果。