实现可编辑 TableView 的问题<ObservableList<String>
Problems implementing an editable TableView<ObservableList<String>
所以我一直在尝试实现一个 TableView,您只需单击它就可以编辑列,然后按回车键保存编辑。我从 线程中的答案中获取了很多代码。这是结果:
import com.sun.javafx.collections.ObservableListWrapper;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.stage.Stage;
public class TestEditableTable extends Application {
public void start(Stage stage) {
TableView<ObservableList<String>> tableView = new TableView<ObservableList<String>>();
tableView.setEditable(true);
// Some dummy data.
ObservableList<ObservableList<String>> dummyData = FXCollections.observableArrayList();
ObservableList<String> firstRow = FXCollections.observableArrayList("Jack", "Smith");
dummyData.add(firstRow);
ObservableList<String> secondRow = FXCollections.observableArrayList("Peter", "Smith");
dummyData.add(secondRow);
TableColumn<ObservableList<String>, String> firstCol = new TableColumn<ObservableList<String>, String>(
"First name");
firstCol.setCellValueFactory(
(TableColumn.CellDataFeatures<ObservableList<String>, String> param) -> new SimpleStringProperty(
param.getValue().get(0)));
TableColumn<ObservableList<String>, String> secondCol = new TableColumn<ObservableList<String>, String>(
"Last name");
secondCol.setCellValueFactory(
(TableColumn.CellDataFeatures<ObservableList<String>, String> param) -> new SimpleStringProperty(
param.getValue().get(1)));
secondCol.setCellFactory(cell -> new EditableCell());
tableView.getColumns().addAll(firstCol, secondCol);
tableView.getItems().addAll(dummyData);
Scene scene = new Scene(tableView);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
class EditableCell extends TableCell<ObservableList<String>, String> {
private TextField textfield = new TextField();
// When the user presses the enter button the edit is saved.
public EditableCell() {
setOnKeyPressed(e -> {
if (e.getCode().equals(KeyCode.ENTER)) {
commitEdit(textfield.getText());
}
});
}
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (isEmpty()) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
textfield.setText(item);
setGraphic(textfield);
setText(null);
} else {
setText(item);
setGraphic(null);
}
}
}
@Override
public void startEdit() {
super.startEdit();
textfield.setText(getItem());
setGraphic(textfield);
setText(null);
}
@Override
public void cancelEdit() {
super.cancelEdit();
setGraphic(null);
setText(getItem());
}
@Override
public void commitEdit(String value) {
super.commitEdit(value);
// This works. But gives me a "Discouraged access: The type 'ObservableListWrapper<String>' is not API (restriction on required library 'C:\Program Files\Java\jre1.8.0_60\lib\ext\jfxrt.jar')".
ObservableListWrapper<String> ob = ((ObservableListWrapper<String>) this.getTableRow().getItem());
ob.set(1, value);
// I had to put this in a Platform.runLater(), otherwise the textfield remained open.
Platform.runLater(() -> {
setText(value);
setGraphic(null);
});
}
}
}
而且这个程序工作正常。当您按下 enter 按钮时,单元格被编辑。但这有两个主要问题:
我按下回车键后,一个单元格就被编辑了。我无法通过单击它再次编辑它。我需要按另一行才能再次编辑它。设置为关注行的父级并没有成功。
commitEdit()
中的代码有效。但它很难看,使用 ObservableListWrapper
会给我警告 "Discouraged access: The type 'ObservableListWrapper' is not API (restriction on required library 'C:\Program Files\Java\jre1.8.0_60\lib\ext\jfxrt.jar')"。此外,单元格索引是硬编码的,如果我在许多不同的列中使用它,它将无法工作。
None 上述问题是可以接受的。
最终实现必须支持限制文本字段中的输入。据我了解,这意味着我需要访问显示在单元格中的 TextField
对象,就像我当前的实现一样。
您正在将事件处理程序注册为 单元格 上的键事件处理程序。在文本字段上注册一个处理程序更有意义,因为这是实际发生感兴趣事件的控件。另请注意,TextField
s 在按下 enter 时触发动作事件。
所以替换
public EditableCell() {
setOnKeyPressed(e -> {
if (e.getCode().equals(KeyCode.ENTER)) {
commitEdit(textfield.getText());
}
});
}
和
public EditableCell() {
textfield.setOnAction(e -> commitEdit(textfield.getText()));
}
这将确保正确维护编辑状态(您的事件处理程序不会发生这种情况,原因我不太清楚),因此它解决了 "re-editing" 的问题。这也意味着您不必手动重置 commitEdit
.
中的文本和图形
对于第二个问题,您可以直接向下转换为 ObservableList<String>
而不是 ObservableListWrapper<String>
。但请注意,您也可以这样做
@Override
public void commitEdit(String value) {
super.commitEdit(value);
ObservableList<String> row = getTableView().getItems().get(getIndex());
row.set(1, value);
}
这不仅避免了 private API,还完全避免了 downcast。
另一种方法是用构建路径中的 JDK 库替换 JRE 库
项目->构建路径->配置构建路径
-1 删除 JRE 库
-2 添加库
-2.1 -> Select Java 与您的 JDK 环境关联的库
所有错误都将消失
所以我一直在尝试实现一个 TableView,您只需单击它就可以编辑列,然后按回车键保存编辑。我从
import com.sun.javafx.collections.ObservableListWrapper;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.stage.Stage;
public class TestEditableTable extends Application {
public void start(Stage stage) {
TableView<ObservableList<String>> tableView = new TableView<ObservableList<String>>();
tableView.setEditable(true);
// Some dummy data.
ObservableList<ObservableList<String>> dummyData = FXCollections.observableArrayList();
ObservableList<String> firstRow = FXCollections.observableArrayList("Jack", "Smith");
dummyData.add(firstRow);
ObservableList<String> secondRow = FXCollections.observableArrayList("Peter", "Smith");
dummyData.add(secondRow);
TableColumn<ObservableList<String>, String> firstCol = new TableColumn<ObservableList<String>, String>(
"First name");
firstCol.setCellValueFactory(
(TableColumn.CellDataFeatures<ObservableList<String>, String> param) -> new SimpleStringProperty(
param.getValue().get(0)));
TableColumn<ObservableList<String>, String> secondCol = new TableColumn<ObservableList<String>, String>(
"Last name");
secondCol.setCellValueFactory(
(TableColumn.CellDataFeatures<ObservableList<String>, String> param) -> new SimpleStringProperty(
param.getValue().get(1)));
secondCol.setCellFactory(cell -> new EditableCell());
tableView.getColumns().addAll(firstCol, secondCol);
tableView.getItems().addAll(dummyData);
Scene scene = new Scene(tableView);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
class EditableCell extends TableCell<ObservableList<String>, String> {
private TextField textfield = new TextField();
// When the user presses the enter button the edit is saved.
public EditableCell() {
setOnKeyPressed(e -> {
if (e.getCode().equals(KeyCode.ENTER)) {
commitEdit(textfield.getText());
}
});
}
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (isEmpty()) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
textfield.setText(item);
setGraphic(textfield);
setText(null);
} else {
setText(item);
setGraphic(null);
}
}
}
@Override
public void startEdit() {
super.startEdit();
textfield.setText(getItem());
setGraphic(textfield);
setText(null);
}
@Override
public void cancelEdit() {
super.cancelEdit();
setGraphic(null);
setText(getItem());
}
@Override
public void commitEdit(String value) {
super.commitEdit(value);
// This works. But gives me a "Discouraged access: The type 'ObservableListWrapper<String>' is not API (restriction on required library 'C:\Program Files\Java\jre1.8.0_60\lib\ext\jfxrt.jar')".
ObservableListWrapper<String> ob = ((ObservableListWrapper<String>) this.getTableRow().getItem());
ob.set(1, value);
// I had to put this in a Platform.runLater(), otherwise the textfield remained open.
Platform.runLater(() -> {
setText(value);
setGraphic(null);
});
}
}
}
而且这个程序工作正常。当您按下 enter 按钮时,单元格被编辑。但这有两个主要问题:
我按下回车键后,一个单元格就被编辑了。我无法通过单击它再次编辑它。我需要按另一行才能再次编辑它。设置为关注行的父级并没有成功。
commitEdit()
中的代码有效。但它很难看,使用ObservableListWrapper
会给我警告 "Discouraged access: The type 'ObservableListWrapper' is not API (restriction on required library 'C:\Program Files\Java\jre1.8.0_60\lib\ext\jfxrt.jar')"。此外,单元格索引是硬编码的,如果我在许多不同的列中使用它,它将无法工作。
None 上述问题是可以接受的。
最终实现必须支持限制文本字段中的输入。据我了解,这意味着我需要访问显示在单元格中的 TextField
对象,就像我当前的实现一样。
您正在将事件处理程序注册为 单元格 上的键事件处理程序。在文本字段上注册一个处理程序更有意义,因为这是实际发生感兴趣事件的控件。另请注意,TextField
s 在按下 enter 时触发动作事件。
所以替换
public EditableCell() {
setOnKeyPressed(e -> {
if (e.getCode().equals(KeyCode.ENTER)) {
commitEdit(textfield.getText());
}
});
}
和
public EditableCell() {
textfield.setOnAction(e -> commitEdit(textfield.getText()));
}
这将确保正确维护编辑状态(您的事件处理程序不会发生这种情况,原因我不太清楚),因此它解决了 "re-editing" 的问题。这也意味着您不必手动重置 commitEdit
.
对于第二个问题,您可以直接向下转换为 ObservableList<String>
而不是 ObservableListWrapper<String>
。但请注意,您也可以这样做
@Override
public void commitEdit(String value) {
super.commitEdit(value);
ObservableList<String> row = getTableView().getItems().get(getIndex());
row.set(1, value);
}
这不仅避免了 private API,还完全避免了 downcast。
另一种方法是用构建路径中的 JDK 库替换 JRE 库 项目->构建路径->配置构建路径 -1 删除 JRE 库
-2 添加库 -2.1 -> Select Java 与您的 JDK 环境关联的库
所有错误都将消失