2017-09-25 73 views
2

我试图提交一个TableCell,但调用了cancelEdit()。这只有在我通过调用tableView.edit()使cell进入其编辑状态时才会发生。尝试提交TableCell时调用cancelEdit()

我调用由editCommit()

private void handleKeyPressed(KeyEvent e) { 
    if (e.getCode() == KeyCode.ENTER) { 
     commitEdit(textField.getText()); 
    } 
} 

为什么按下回车键时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);} 
} 
+0

请问为什么你重写的CancelEdit( ) 方法?通过删除它,问题就解决了,并且更改已被提交 – JKostikiadis

+0

@JKostikiadis如果您不按回车键单击其他单元格,则最初单击的单元格仍会显示文本字段。 CancelEdit()调用setContentDisplay(ContentDisplay.TEXT_ONLY); – jpell

+0

所以你想检查,如果没有按下输入字段失去焦点,如果是这样,只是放弃更改..我看到让我考虑一下 – JKostikiadis

回答

1

基本上,当涉及到terminating an on-going edit时,TableView(和其他虚拟化控件)非常不卫生:大多数情况下,当可用性需要时取消编辑一个提交。

在您的例子,有两个不同的(著名)的原因

  1. 编辑是当项目列表被修改的肠子深处取消,这里的项目添加
  2. 编辑由编辑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 

为了说明问题2:

  • 添加项目和编辑任何
  • 单击Edit开始编辑另一个
  • 预期:首先编辑保存,第二编辑开始
  • 实际:第一编辑取消,第二个编辑开始

随着打印出来:

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);} 
} 
+0

计划是让整行进入编辑状态,这会在添加新项目时使多个单元格全部进入编辑状态。我不能再使用TableView.edit(),因为它会在先前的单元格上调用cancelEdit()。因此,看看如何实现这一点很有意思。 – jpell

+0

@jpell不打架系统 - 它的设计目的是在编辑状态下拥有零个或正好一个单元格! – kleopatra

0

在你的代码的问题是buildControl()方法,而不是用你创建一个新的每次触发一个新的编辑事件时间之前的文本字段内造成的。您所要做的就是将TextField的初始化移入EditingStringCell构造函数中。

我测试了所有(我希望)的情况。编辑时添加新行,编辑时选择另一行,提交更改一切正常。这里几乎是早上6点,所以我的想法并不十分清楚:P

+0

是的,我看到你的逻辑。但保持简单;)帮助 – JKostikiadis

相关问题