原文位址http://download.oracle.com/javafx/2.0/ui_controls/table-view.htm
JavaFX SDK API在的好幾個類都被設計來以表格形式呈現資料。在 JavaFX應用中建立表格的最重要類是
TableView
, TableColumn
和TableCell
。可以通過實作資料模型或者應用一個細胞工廠來産生表格。
表格的類提供了内置的功能來在必要的時候進行資料排序和重置大小。
Figure 13-1 是一個典型的表格,用來呈現位址簿中的聯系人資訊。 Figure 13-1 Table Sample Description of "Figure 13-1 Table Sample"建立Table
Example 13-1中的代碼塊建立了一個空表格,它帶有3列。然後被加入了應用的場景中。
Example 13-1 Adding a Table
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 Main 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(400);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
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.getChildren().addAll(label, table);
vbox.setPadding(new Insets(10, 0, 0, 10));
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
}
表格控件是通過執行個體化
TableView
類建立的。在
中,它被加入到了
VBox
布局容器中,然而,你可以直接把它加入應用場景中。
定義了三列來存儲位址簿中的以下資訊:某個聯系人的名和姓還有電郵位址。列是用
TableColumn
類建立的。
TableView
類的
getColumns
方法把前面建立的列加入到表格中。在應用中,可以用這個方法動态的添加和移除列。
編譯運作的效果如下
Figure 13-2.
Figure 13-2 Table Without Data
Description of "Figure 13-2 Table Without Data"可以通過調用
setVisible
方法來管理列的可視性。比如說,你應用的邏輯要求隐藏使用者電郵位址,可以這樣達到目的
:emailCol.setVisible(false)
如果資料要求更複雜的資料呈現結構,可以建立内嵌的列。
比如,如果位址簿中的聯系人有兩個email賬戶,就需要兩列來展示首選和次要位址了。建立兩個子列,然後在
emailCol
上調用
getColumns
方法,見
Example 13-2Example 13-2 Creating Nested Columns
TableColumn firstEmailCol = new TableColumn("Primary");
TableColumn secondEmailCol = new TableColumn("Secondary");
emailCol.getColumns().addAll(firstEmailCol, secondEmailCol);
把這些代碼加入到
, 然後編譯運作,表格的呈現效果如
Figure 13-3Figure 13-3 Table with Nested Columns
Description of "Figure 13-3 Table with Nested Columns"盡管表格被加入到了應用中,标準标題依然顯示的是"No content in table" 因為沒定義資料。為了不顯示這個标題,可以使用
setPlaceholder方法指定一個
Node
對象來顯示在空表格中。
定義Data Model
當在JavaFX應用中建立表格時,最佳實踐是實作一個定義了資料模型、提供了方法和字段的類來擴充表格的工作。
Example 13-3建立了一個
Person類來定義位址簿中的資料。
Example 13-3 Creating the Person Class
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
字元串屬性(string property)是建立來引用特定的資料元素的。
另外,
get和
set方法是提供給每個資料元素的。這樣,比如說,
getFirstName方法傳回了
firstName屬性的值,而
setFirstName
方法為這個屬性指定了值。
當資料模型在
Person
類中形成時,可以建立一個
ObservableList
數組來定義足夠多的行來在表格中顯示你的資料。看
Example 13-4中的代碼。
Example 13-4 Defining Table Data in an Observable List
final ObservableList<Person> 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]")
);
下一步是将資料和表格列相關聯。可以通過為每個資料元素定義的屬性來實作,見
Example 13-5Example 13-5 Setting Data Properties to Columns
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相應的方法。
定義了資料模型、加入資料并和列相關聯後可以把資料加入表格了。使用
TableView
setItems
方法
:table.setItems(data)
由于
ObservableList對象可以跟蹤元素的任何改變,
TableView的内容在資料改變後是自動更新的。
檢視
Example 13-6Example 13-6 Creating a Table and Adding Data to It
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.control.cell.PropertyValueFactory;
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.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class Main extends Application {
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);
}
}
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> 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(400);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person,String>("firstName")
);
TableColumn lastNameCol = new TableColumn("Last Name");
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.getChildren().addAll(label, table);
vbox.setPadding(new Insets(10, 0, 0, 10));
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
}
編譯運作的效果如圖
Figure 13-4所示。
Figure 13-4 Table Populated with Data
Description of "Figure 13-4 Table Populated with Data"新增行
中的表格包含了5行,目前還無法更改。
可以使用文本框來輸入First Name, Last Name和 Email 列中的内容。Text Field控件使你的應用能夠接收使用者的輸入。Example 13-7建立了三個文本框并分别定義了提示語,還建立了一個Add按鈕。
Example 13-7 Using Text Fields to Enter New Items in the Table
final TextField addFirstName = new TextField();
addFirstName.setPromptText("Last 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.setText("");
addLastName.setText("");
addEmail.setText("");
}
});
點選 Add按鈕後,文本框中的值就包含進一個
Person
的構造方法并加入到
data可見清單(
observable list
)中。這樣
,新輸入的聯系人資訊就顯示在表格中了。
Example 13-8Example 13-8 Table with the Text Fields to Enter New Items
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
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 Main extends Application {
public static class Person {
private final StringProperty firstName;
private final StringProperty lastName;
private final StringProperty 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);
}
}
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> 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]")
);
private 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(400);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
TableColumn firstNameCol = new TableColumn("First");
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person,String>("firstName")
);
TableColumn lastNameCol = new TableColumn("Last");
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("Last 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.setText("");
addLastName.setText("");
addEmail.setText("");
}
});
hb.getChildren().addAll(addFirstName, addLastName, addEmail, addButton);
hb.setSpacing(3);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.getChildren().addAll(label, table, hb);
vbox.setPadding(new Insets(10, 0, 0, 10));
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
}
應用并沒有提供任何過濾器來檢查輸入(比如輸入的電郵位址并不符合正确形式)。 你可以在開發的時候自己加上這些功能。
目前應用也不能檢查十分輸入了空值。如果沒輸入内容,點選Add按鈕會加入空行。
Figure 13-5示範了使用者如何新增行。
Figure 13-5 Adding Contact Information to the Address Book
Description of "Figure 13-5 Adding Contact Information to the Address Book" Figure 13-6是上圖點選Add按鈕後的效果。聯系人Emma White的資訊現在在表格中顯示了。
Figure 13-6 Newly Added Entry
Description of "Figure 13-6 Newly Added Entry"資料排序
TableView
類提供了内置的資料排序能力。。使用者可以點選列标題來改變資料順序。點選一次是遞增排序,點選兩次是遞減排序,點選三次是不能排序。預設地,是沒有排序。
使用者可以萬惡表格中的多個列進行排序,并在排序操作中指定各列的優先級。要排序多列,在點選列标題的時候按住Shift鍵即可。
在
Figure 13-7中,第一列應用了升序,第二列是降序。注意第一列的優先級要高于第二列。
Figure 13-7 Sorting Multiple Columns
Description of "Figure 13-7 Sorting Multiple Columns"作為開發者,可以通過
setSortType方法為應用中的每一列設定排序參數。
可以指定是升序還是降序。比如,下面這行代碼設定了emailCol列是降序排序
:emailCol.setSortType(TableColumn.SortType.DESCENDING);
可以通過從
TableView.sortOrder可見清單增加或删除
TableColumn執行個體來指定要排序哪些列。該清單中列的順序就是排序的優先級
(比如,0項目的優先級高于第一個項目。)
使用
setSortable(false)方法可以阻止列的排序。
編輯Table中的資料
TableView類不僅顯示表格資料,也提供了編輯資料的功能。可以使用
TableView.edit(int row, TableColumn<S,?> column)
方法開始編輯。也可以使用
TableCell類的方法編輯表格資料。見
Example 13-9 Example 13-9 Implementing Cell Editing
class EditingCell extends TableCell<Person, String> {
private TextField textField;
public EditingCell() {
}
@Override
public void startEdit() {
super.startEdit();
if (isEmpty()) {
return;
}
if (textField == null) {
createTextField();
} else {
textField.setText(getItem());
}
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
@Override
public void cancelEdit() {
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!isEmpty()) {
if (textField != null) {
textField.setText(item);
}
setText(item);
}
}
private void createTextField() {
textField = new TextField(getItem());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
@Override public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(textField.getText());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
}
}
中
,createTextField方法使用了
textField變量來分析輸入串并調用
commitEdit或
cancelEdit
方法(取決于按下了 Enter還是Escape鍵)
setCellFactory
方法能用來建立定制的細胞工廠。
定制細胞工廠的首要任務是無論何時請求多傳回一個建立的TableCell執行個體。
Example 13-10 展示了如何為
firstNameCol
lastNameCol
和
emailCol列實作細胞工廠。
Example 13-10 Using a Cell Factory
Callback<TableColumn, TableCell> cellFactory =
new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
return new EditingCell();
}
};
firstNameCol.setCellFactory(cellFactory);
lastNameCol.setCellFactory(cellFactory);
emailCol.setCellFactory(cellFactory);
用
setOnEditCommit方法,如
Example 13-11 所示,這樣表格能處理項目的任何改變。該方法辨別了一個編輯過的項目,取回了新資料,代替了
data
可見清單的相應資料。
Example 13-11 Processing Edited Data in the Table
//Enabling editing
table.setEditable(true);
//Modifying the firstName property
firstNameCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {
@Override public void handle(CellEditEvent<Person, String> t) {
((Person)t.getTableView().getItems().get(
t.getTablePosition().getRow())).setFirstName(t.getNewValue());
}
});
//Modifying the lastName property
lastNameCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {
@Override public void handle(CellEditEvent<Person, String> t) {
((Person)t.getTableView().getItems().get(
t.getTablePosition().getRow())).setLastName(t.getNewValue());
}
});
//Modifying the email property
emailCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {
@Override public void handle(CellEditEvent<Person, String> t) {
((Person)t.getTableView().getItems().get(
t.getTablePosition().getRow())).setEmail(t.getNewValue());
}
});
在 Figure 13-8中,使用者編輯了Michael Brown的姓,輸入了新值并按了回車。回車後就不能再編輯了,這種行為由
TextField類的實作決定。
Figure 13-8 Editing a Table Cell
Description of "Figure 13-8 Editing a Table Cell"