Java 尝试提交TableCell时调用cancelEdit()

Java 尝试提交TableCell时调用cancelEdit(),java,javafx,tableview,Java,Javafx,Tableview,我正在尝试提交一个TableCell,但是调用了cancelEdit()。只有通过调用tableView.edit()使单元格进入编辑状态,才会发生这种情况 我通过以下方式调用editCommit(): private void handleKeyPressed(KeyEvent e) { if (e.getCode() == KeyCode.ENTER) { commitEdit(textField.getText()); } } 为什么按下enter键时会调

我正在尝试提交一个
TableCell
,但是调用了
cancelEdit()
。只有通过调用
tableView.edit()
使
单元格进入编辑状态,才会发生这种情况

我通过以下方式调用
editCommit()

private void handleKeyPressed(KeyEvent e) {
    if (e.getCode() == KeyCode.ENTER) {
        commitEdit(textField.getText());
    }
}
为什么按下enter键时会调用
cancelEdit()
?手动单击添加的空行,然后按enter键尝试提交单元格可以正常工作

import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.control.TableView;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.beans.property.SimpleStringProperty;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        BorderPane root = new BorderPane();
        primaryStage.setScene(new Scene(root, 400, 400));
        primaryStage.show();
        root.setCenter(new TableExample());
    }

    public class TableExample extends VBox {
        public TableExample() {
            TableView<Building> tableView = new TableView<>();
            tableView.setEditable(true);

            //adds a new blank row to table;
            Button add = new Button("add");
            add.setOnAction(e -> {
                Building newBuilding = new Building();
                tableView.getItems().add(0, newBuilding);
                tableView.edit(0, tableView.getColumns().get(0));

                //I tried focusing and selecting the row
                //but does not help.
                tableView.getFocusModel().focus(0);
                tableView.getSelectionModel().select(0);

            });

            getChildren().addAll(add, tableView);

            Building building = new Building();
            building.setName("Building 100");

            Function<Building, StringProperty> property = Building::nameProperty;

            TableColumn<Building, String> column = new TableColumn<>("name");
            column.setEditable(true);
            column.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
            column.setCellFactory(p -> new EditingStringCell<>());

            tableView.getColumns().add(column);
            tableView.setItems(FXCollections.observableArrayList(building));
        }
    }

    public class EditingStringCell<S, T> extends TableCell<Building, String> {
        private TextField textField;

        public EditingStringCell() {
            setEditable(true);
        }

        @Override
        protected void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);
            setText(empty ? null : item);
        }

        @Override
        public void startEdit() {
            System.out.println("start edit");
            super.startEdit();
            buildControl(getItem());
            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
        }

        private void buildControl(String item) {
            System.out.println("item: " + item);
            textField = new TextField();        
            textField.addEventHandler(KeyEvent.KEY_RELEASED, 
            this::handleKeyPressed);
            textField.setText(item == null ? "" : (item));
            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
            setGraphic(textField);
        }

        //whenever pressing enter on a new "blank" row
        //cancelEdit() gets called. But if you try again 
        //it works.
        private void handleKeyPressed(KeyEvent e) {
            if (e.getCode() == KeyCode.ENTER) {
                commitEdit(textField.getText());
            }
        }

        @Override
        public void cancelEdit() {
            System.out.println("cancel edit");
            super.cancelEdit();
            setContentDisplay(ContentDisplay.TEXT_ONLY);
        }

        @Override
        public void commitEdit(String newValue) {
            System.out.println("commited: " + newValue);
            super.commitEdit(newValue);
            setContentDisplay(ContentDisplay.TEXT_ONLY);
        }
    }

    public class Building {
        private final StringProperty name = new SimpleStringProperty();
        public StringProperty nameProperty() {
            return name;
        }
        public String getName() {
            return name.get();
        }
        public void setName(String name) {
            this.name.set(name);
        }
    }
    public static void main(String[] args) {launch(args);}
}
import java.util.function.function;
导入javafx.application.application;
导入javafx.beans.property.StringProperty;
导入javafx.collections.FXCollections;
导入javafx.stage.stage;
导入javafx.scene.scene;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.layout.VBox;
导入javafx.scene.control.TableView;
导入javafx.scene.control.Button;
导入javafx.scene.control.ContentDisplay;
导入javafx.scene.control.TableCell;
导入javafx.scene.control.TableColumn;
导入javafx.scene.control.TextField;
导入javafx.scene.input.KeyCode;
导入javafx.scene.input.KeyEvent;
导入javafx.beans.property.SimpleStringProperty;
公共类主扩展应用程序{
@凌驾
公共无效开始(阶段primaryStage){
BorderPane根=新的BorderPane();
原始阶段。设置场景(新场景(根,400400));
primaryStage.show();
setCenter(新的TableExample());
}
公共类TableExample扩展了VBox{
公共表格示例(){
TableView TableView=新建TableView();
tableView.setEditable(true);
//将新的空行添加到表中;
按钮添加=新按钮(“添加”);
添加.设置操作(e->{
建筑新建筑=新建筑();
tableView.getItems().add(0,新建);
编辑(0,tableView.getColumns().get(0));
//我试着聚焦并选择那一行
//但是没有帮助。
tableView.getFocusModel().focus(0);
tableView.getSelectionModel().select(0);
});
getChildren().addAll(add,tableView);
建筑物=新建筑物();
建筑物名称(“100号建筑物”);
函数属性=Building::nameProperty;
TableColumn=新的TableColumn(“名称”);
column.setEditable(true);
column.setCellValueFactory(cellData->property.apply(cellData.getValue());
column.setCellFactory(p->neweditingString());
tableView.getColumns().add(column);
tableView.setItems(FXCollections.observearraylist(building));
}
}
公共类EditingString单元格扩展了TableCell{
私有文本字段文本字段;
公共编辑字符串单元格(){
可编辑设置(真);
}
@凌驾
受保护的void updateItem(字符串项,布尔值为空){
super.updateItem(项,空);
setText(空?空:项);
}
@凌驾
公开作废已启动IT(){
System.out.println(“开始编辑”);
super.startEdit();
buildControl(getItem());
setContentDisplay(仅限ContentDisplay.GRAPHIC_);
}
私有void buildControl(字符串项){
系统输出打印项次(“项:+项);
textField=新的textField();
textField.addEventHandler(KeyEvent.KEY_已发布,
此::handleKeyPressed);
textField.setText(item==null?”:(item));
setContentDisplay(仅限ContentDisplay.GRAPHIC_);
设置图形(文本字段);
}
//每当在新的“空白”行上按enter键时
//将调用cancelEdit()。但如果重试
//它起作用了。
私有无效手柄按键按下(按键事件e){
如果(例如getCode()==KeyCode.ENTER){
committedit(textField.getText());
}
}
@凌驾
公共作废取消编辑(){
System.out.println(“取消编辑”);
super.cancelEdit();
setContentDisplay(仅限ContentDisplay.TEXT_);
}
@凌驾
public void committedit(字符串newValue){
System.out.println(“提交:+newValue”);
super.committedit(newValue);
setContentDisplay(仅限ContentDisplay.TEXT_);
}
}
公共班级大楼{
私有最终StringProperty名称=新SimpleStringProperty();
公共字符串属性nameProperty(){
返回名称;
}
公共字符串getName(){
返回name.get();
}
公共void集合名(字符串名){
this.name.set(name);
}
}
公共静态void main(字符串[]args){launch(args);}
}

代码中的问题是在buildControl()方法中引起的,而不是在每次触发新编辑事件时创建新的文本字段时使用以前的文本字段。您所要做的就是在EditingString构造函数中移动TextField的初始化

我测试了所有(我希望)情况。编辑时添加新行,编辑时选择另一行,提交更改一切正常。几乎是早上6点,所以我的想法不是很清楚:基本上,TableView(和其他虚拟化控件)在以下方面表现得很差:大多数时候,当可用性需要提交时,它会取消编辑

在您的示例中,有两个不同(众所周知)的原因

  • 当项目列表被修改时,在肠道深处的编辑被取消,在这里添加了一个项目
  • 当编辑tableCell检测到表的编辑位置发生更改时,它将取消编辑
  • 为了查看,我在您的代码中添加了一些打印:

    要演示问题1:

    • 编辑第一项
    • 点击
      start edit 0 on id: 2
      before adding: TablePosition [ row: 0, column: ... ]
      cancel edit 0 on id: 2
      start edit 0 on id: 2
      start edit 0 on id: 14   // <- some slightly weird cell re-use
      cancel edit 12 on id: 2  // <- triggers a cancel on the old, functional interference unknown 
      
      before starting edit on : 1
      start edit 1 on id: 13
      cancel edit 0 on id: 14
      
      import java.util.function.Function;
      
      import javafx.application.Application;
      import javafx.beans.property.SimpleStringProperty;
      import javafx.beans.property.StringProperty;
      import javafx.collections.FXCollections;
      import javafx.event.ActionEvent;
      import javafx.scene.Parent;
      import javafx.scene.Scene;
      import javafx.scene.control.Button;
      import javafx.scene.control.ContentDisplay;
      import javafx.scene.control.TableCell;
      import javafx.scene.control.TableColumn;
      import javafx.scene.control.TablePosition;
      import javafx.scene.control.TableView;
      import javafx.scene.control.TextField;
      import javafx.scene.input.KeyCode;
      import javafx.scene.input.KeyEvent;
      import javafx.scene.layout.BorderPane;
      import javafx.scene.layout.VBox;
      import javafx.stage.Stage;
      
      /**
       * https://stackoverflow.com/q/46396423/203657
       */
      public class TableEditSO extends Application {
      
          @Override
          public void start(Stage primaryStage) {
              BorderPane root = new BorderPane();
              primaryStage.setScene(new Scene(root, 400, 400));
              primaryStage.show();
              //root.setCenter(new TableExample());
              root.setCenter(getContent());
          }
      
          private Parent getContent() {
              TableView<Building> tableView = new TableView<>();
              tableView.setEditable(true);
      
              //adds a new blank row to table;
              Button add = new Button("add");
              add.setOnAction(e -> {
                  Building newBuilding = new Building();
                  // modifications to the items will cancel an edit
                  System.out.println("before adding: " + tableView.getEditingCell());
                  tableView.getItems().add(0, newBuilding);
                  tableView.edit(0, tableView.getColumns().get(0));
      
                  //I tried focusing and selecting the row
                  //but does not help.
                  tableView.getFocusModel().focus(0);
                  tableView.getSelectionModel().select(0);
      
              });
      
              Button edit = new Button("edit");
              edit.setOnAction(e -> {
                  int size = tableView.getItems().size();
                  if (size < 2) return;
                  TablePosition pos = tableView.getEditingCell();
                  int last = size - 1;
                  if (pos != null) {
                      int row = pos.getRow();
                      if (row == last) {
                          //make certain we switch editing to another row
                          last--;
                      }
                  }
                  System.out.println("before starting edit on : " + last);
                  // starting edit on another item will cancel an edit
                  tableView.edit(last, tableView.getColumns().get(0));
              });
      
              Building building = new Building();
              building.setName("Building 100");
      
              Function<Building, StringProperty> property = Building::nameProperty;
      
              TableColumn<Building, String> column = new TableColumn<>("name");
              column.setEditable(true);
              column.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
              column.setCellFactory(p -> new EditingStringCell<>());
      
              tableView.getColumns().add(column);
              tableView.setItems(FXCollections.observableArrayList(building));
      
              VBox box = new VBox(10, add, edit, tableView);
              return box;
          }
      
          // don't extend without functional reason
          public class TableExample extends VBox {
              public TableExample() {
                  TableView<Building> tableView = new TableView<>();
                  tableView.setEditable(true);
      
                  //adds a new blank row to table;
                  Button add = new Button("add");
                  add.setOnAction(e -> {
                      Building newBuilding = new Building();
                      System.out.println("before adding: " + tableView.getEditingCell());
      
                      tableView.getItems().add(0, newBuilding);
                      System.out.println("after adding: " + tableView.getEditingCell());
                      tableView.edit(0, tableView.getColumns().get(0));
                      System.out.println("after starting: " + tableView.getEditingCell());
      
                      //I tried focusing and selecting the row
                      //but does not help.
                      tableView.getFocusModel().focus(0);
                      tableView.getSelectionModel().select(0);
      
                  });
      
                  getChildren().addAll(add, tableView);
      
                  Building building = new Building();
                  building.setName("Building 100");
      
                  Function<Building, StringProperty> property = Building::nameProperty;
      
                  TableColumn<Building, String> column = new TableColumn<>("name");
                  column.setEditable(true);
                  column.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
                  column.setCellFactory(p -> new EditingStringCell<>());
      
                  tableView.getColumns().add(column);
                  tableView.setItems(FXCollections.observableArrayList(building));
              }
          }
      
          public static class EditingStringCell<S, T> extends TableCell<Building, String> {
              static int id ;
              private int myId;
              private TextField textField;
      
              public EditingStringCell() {
                  // just to see re-use of cells
                  myId = id++;
                  setContentDisplay(ContentDisplay.TEXT_ONLY);
                  // not needed, it's true by default
                  // setEditable(true);
              }
      
              @Override
              protected void updateItem(String item, boolean empty) {
                  super.updateItem(item, empty);
                  setText(empty ? null : item);
              }
      
              @Override
              public void startEdit() {
                  super.startEdit();
                  if (isEditing()) {
                      System.out.println("start edit " + getIndex() + " on id: " + myId);
                      updateEditControl(getItem());
      
                  }
              }
      
              private void updateEditControl(String item) {
                  if (textField == null) {
                      textField = new TextField();   
                      // always use the highest abstract available
                      // so use action instead of keyEvent
                      textField.setOnAction(this::handleAction);
                      //textField.addEventHandler(KeyEvent.KEY_PRESSED, 
                      //        this::handleKeyPressed);
                      setGraphic(textField);
                  }
                  textField.setText(item == null ? "" : (item));
                  setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
              }
      
              private void handleAction(ActionEvent ae) {
                  commitEdit(textField.getText());
      
              }
      
              //whenever pressing enter on a new "blank" row
              //cancelEdit() gets called. But if you try again 
              //it works.
              private void handleKeyPressed(KeyEvent e) {
                  if (e.getCode() == KeyCode.ENTER) {
                      commitEdit(textField.getText());
                  }
              }
      
              @Override
              public void cancelEdit() {
                  super.cancelEdit();
                  System.out.println("cancel edit " + getIndex() + " on id: " + myId);
                  setContentDisplay(ContentDisplay.TEXT_ONLY);
              }
      
              @Override
              public void commitEdit(String newValue) {
                  super.commitEdit(newValue);
                  System.out.println("commited: " + newValue + " index: " + getIndex() + " on id: " + myId);
                  setContentDisplay(ContentDisplay.TEXT_ONLY);
              }
          }
      
          public class Building {
              private final StringProperty name = new SimpleStringProperty();
              public StringProperty nameProperty() {
                  return name;
              }
              public String getName() {
                  return name.get();
              }
              public void setName(String name) {
                  this.name.set(name);
              }
          }
          public static void main(String[] args) {launch(args);}
      }