1.定义数据模型(Data Model)
- 当你在JavaFX应用程序中创建表格时,一个最佳实践是:创建一个类用于定义数据模型并且提供方法和属性。
- 事实上这个也符合MVC设计模式
- 例子中的Property类型,详见后续文章
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
}
2.创建OBservableList
- ObservableList对象具有自动追踪其包含元素的改变的功能,所以TableView的内容将会在数据改变时自动更新。
final ObservableList<Person> data = FXCollections.observableArrayList(
new Person(),
new Person(),
new Person(),
new Person(),
new Person()
);
3.创建TableColumn并添加进TableView
- 这一步可以通过FXML文件来实现。也可以通过如下代码来实现
- 不指明TableColumn的数据类型
TableColumn firstNameCol = new TableColumn("FirstName");
- 指明TableColumn的数据类型
TableColumn<Person, String> firstNameCol = new TableColumn<>("FirstName");
- 在FXML中定义一个TableColmn。注意在controller中定义的TableColumn可以带有泛型
@FXML
private TableColumn<DocArg,String> argValueColumn;
- 添加TableColumn进TableView。如果是在FXML中创建的。这个步骤可以省略
tableView.getColumns().add(tableColumn);
4.把数据关联到表格的列。
- setCellValueFactory方法为每列指定了一个单元格工厂(cell factory),这些cell factory是通过PropertyValueFactory类来实现的,它将Person类中对应的属性映射到对应的表格列中。
- 当数据模型被定义完毕,并且数据被关联到列之后,你可以通过TableView类的setItems方法来向表格中添加数据:如:table.setItems(data)。
- 如果没有指明TableColumn的数据类型:
firstNameCol.setCellValueFactory(
new PropertyValueFactory<>("firstName")
);
- 指明TableColumn的数据类型的时候。注意getValue()方法。
- 当数据模型里面包含对象的时候,为了访问这个嵌套对象,就需要用这个方式。
tableColumn.setCellValueFactory(tf -> tf.getValue().getArgsList().get(1).valueProperty());
5.增加删除行
- 事实上是通过操作设置给TableView对象的OBservableList来实现的。
- ObservableList对象具有自动追踪其包含元素的改变的功能,所以TableView的内容将会在数据改变时自动更新。
6.编辑单一单元格内容
- 使用setCellFactory,指定TextFieldTableCell类来使其变成一个文本域。
- setOnEditCommit方法处理编辑过程,并且将更新后的值分配给对应的表格单元格(事实上是传给对应的model)
firstNameCol.setCellFactory(TextFieldTableCell.<Person>forTableColumn());
firstNameCol.setOnEditCommit(
(CellEditEvent<Person, String> t) -> {
((Person) t.getTableView().getItems().get(t.getTablePosition().getRow()))
.setFirstName(t.getNewValue());
});
7.ComboBoxCell指定选项值
typeColumn.setCellFactory(t->{
ObservableList<String> typeList = FXCollections.observableArrayList("单行输入",
"多行输入",
"单选输入",
"今天日期",
"昨天日期",
"明天日期");
ComboBoxTableCell<DocArg,String > comboBoxTableCell = new ComboBoxTableCell<>(typeList);
return comboBoxTableCell;
});
//简单版本
outageTypeColumn.setCellFactory(t-> new ComboBoxTableCell<>(MissionTextParser.MISSION_OUTAGE));
8.设置列自动铺满可用控件
8.1代码中设置
Column Resize Policy //在Scene Builder软件的控件属性设置项里面。修改成 constrained-resize。确保这个TableView的列能够铺满所有的可用空间。
8.2. FXML文件中如下修改
<TableView >
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
9. 添加序号列
- DocType替换为数据模型类即可
noColumn.setCellFactory((col) ->{
TableCell<DocType, String> cell = new TableCell<DocType, String>() {
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
this.setText(null);
this.setGraphic(null);
if (!empty) {
int rowIndex = this.getIndex() + 1;
this.setText(String.valueOf(rowIndex));
}
}};
return cell;
});
10. 获得用户选择的行
int recordIndex = recordTableView.getSelectionModel().getSelectedIndex();
11.修改列的宽度
noColumn.setMaxWidth(50);
checkedCol.setMaxWidth(50);
noColumn.setMinWidth(50);
checkedCol.setMinWidth(50);
11.增加选择框
-
效果如下
11.1 CheckBoxTableCell类
/* CheckBoxTableCell.java 1.0 2010-2-2
*
* Copyright (c) 2012 by Chen Zhiwu
* All rights reserved.
*
* The copyright of this software is own by the authors.
* You may not use, copy or modify this software, except
* in accordance with the license agreement you entered into
* with the copyright holders. For details see accompanying license
* terms.
*/
public class CheckBoxTableCell<S, T> extends TableCell<S, T> {
private final CheckBox checkBox;
private final boolean showText;
private final Callback<T, String> toString;
private final Callback<Integer, ObservableValue<Boolean>> getSelectedProperty;
private ObservableValue<Boolean> booleanProperty;
public CheckBoxTableCell() {
this(null, null);
}
public CheckBoxTableCell(
Callback<Integer, ObservableValue<Boolean>> toString) {
this(toString, null);
}
public CheckBoxTableCell(
Callback<Integer, ObservableValue<Boolean>> getSelectedProperty,
Callback<T, String> toString) {
this.getSelectedProperty = getSelectedProperty;
this.toString = toString;
this.showText = toString != null;
this.checkBox = new CheckBox();
setAlignment(Pos.CENTER);
setGraphic(checkBox);
if (showText) {
checkBox.setAlignment(Pos.CENTER_LEFT);
}
}
public CheckBoxTableCell(
Callback<T, ObservableValue<Boolean>> callback,
Callback<Integer, ObservableValue<Boolean>> getSelectedProperty,
Callback<T, String> toString) {
this.getSelectedProperty = getSelectedProperty;
this.toString = toString;
this.showText = toString != null;
this.checkBox = new CheckBox();
setAlignment(Pos.CENTER);
setGraphic(checkBox);
if (showText) {
checkBox.setAlignment(Pos.CENTER_LEFT);
}
}
@Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
return;
}
if (this.showText) {
setText(this.toString.call(item));
}
setGraphic(this.checkBox);
if (this.booleanProperty instanceof BooleanProperty)
this.checkBox.selectedProperty().unbindBidirectional(
(BooleanProperty) this.booleanProperty);
ObservableValue localObservableValue = getSelectedProperty();
if (localObservableValue instanceof BooleanProperty) {
this.booleanProperty = localObservableValue;
this.checkBox.selectedProperty().bindBidirectional(
(BooleanProperty) this.booleanProperty);
}
this.checkBox.visibleProperty().bind(getTableView().editableProperty()
.and(getTableColumn().editableProperty())
.and(editableProperty()));
};
private ObservableValue getSelectedProperty() {
return ((this.getSelectedProperty != null) ? (ObservableValue) this.getSelectedProperty
.call(Integer.valueOf(getIndex())) : getTableColumn()
.getCellObservableValue(getIndex()));
}
}
11.2CellFactory类
public class CellFactory {
// table check box
public static <S> Callback<TableColumn<S, Boolean>, TableCell<S, Boolean>> tableCheckBoxColumn() {
return tableCheckBoxColumn(null, null);
}
public static <S, T> Callback<TableColumn<S, T>, TableCell<S, T>> tableCheckBoxColumn(
Callback<Integer, ObservableValue<Boolean>> paramCallback) {
return tableCheckBoxColumn(paramCallback, null);
}
public static <S, T> Callback<TableColumn<S, T>, TableCell<S, T>> tableCheckBoxColumn(
Callback<Integer, ObservableValue<Boolean>> paramCallback,
boolean paramBoolean) {
Callback<T, String> callback = new Callback<T, String>() {
@Override
public String call(T t) {
return ((t == null) ? "" : t.toString());
}
};
return tableCheckBoxColumn(paramCallback, callback);
}
public static <S, T> Callback<TableColumn<S, T>, TableCell<S, T>> tableCheckBoxColumn(
final Callback<Integer, ObservableValue<Boolean>> getSelectedProperty,
final Callback<T, String> toString) {
return new Callback<TableColumn<S, T>, TableCell<S, T>>() {
@Override
public TableCell<S, T> call(TableColumn<S, T> paramTableColumn) {
return new CheckBoxTableCell<S,T>(getSelectedProperty,toString);
}
};
}
}
11.3使用方法
- 把FileWrap换成对应的Model即可。
- 其中的filetable由fxml文件获取。
- 注意checkedCol.setCellValueFactory(new PropertyValueFactory<>("checked"));需要指定model正确的BooleanProperty属性名称,否则无法双向绑定更新
- 注意g.isChecked()需要和model一致
public class FileWrap {
private StringProperty fileName;
private BooleanProperty checked;
public String getFileName() {
return fileName.get();
}
public StringProperty fileNameProperty() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName.set(fileName);
}
public boolean isChecked() {
return checked.get();
}
public BooleanProperty checkedProperty() {
return checked;
}
public void setChecked(boolean checked) {
this.checked.set(checked);
}
public FileWrap() {
this.checked = new SimpleBooleanProperty(false);
this.fileName = new SimpleStringProperty("/");
}
}
public class MainController implements Initializable {
ObservableList<FileWrap> fileWraps = FXCollections.observableArrayList();
@FXML
private TableView<FileWrap> filesTable;
@Override
public void initialize(URL location, ResourceBundle resources) {
//选择列
TableColumn<FileWrap, Boolean> checkedCol= new TableColumn<>("选择");
checkedCol.setCellValueFactory(new PropertyValueFactory<>("checked"));
checkedCol.setCellFactory(CellFactory.tableCheckBoxColumn(new Callback<Integer, ObservableValue<Boolean>>() {
@Override
public ObservableValue<Boolean> call(Integer index) {
final FileWrap g= filesTable.getItems().get(index);
ObservableValue<Boolean> retval = new SimpleBooleanProperty(g,"available",g.isChecked());
retval.addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable,Boolean oldValue, Boolean newValue) {
g.setChecked(newValue);
}
});
return retval;
}
}));
//文件名列
TableColumn<FileWrap, String> fileNameCol= new TableColumn<>("文件名称");
fileNameCol.setCellValueFactory(new PropertyValueFactory<>("fileName"));
//序号列
TableColumn<FileWrap, String> noColumn= new TableColumn<>("序号");
noColumn.setCellFactory((col) ->{
TableCell<FileWrap, String> cell = new TableCell<FileWrap, String>() {
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
this.setText(null);
this.setGraphic(null);
if (!empty) {
int rowIndex = this.getIndex() + 1;
this.setText(String.valueOf(rowIndex));
}
}};
return cell;
});
noColumn.setMaxWidth(50);
checkedCol.setMaxWidth(50);
noColumn.setMinWidth(50);
checkedCol.setMinWidth(50);
filesTable.getColumns().add(noColumn);
filesTable.getColumns().add(checkedCol);
filesTable.getColumns().add(fileNameCol);
filesTable.setItems(fileWraps);
}
}