【问题标题】:How to make a JavaFX TableView cell editable without first pressing Enter?如何在不先按 Enter 的情况下使 JavaFX TableView 单元格可编辑?
【发布时间】:2014-03-26 02:28:43
【问题描述】:

我正在学习 JavaFX 并实现一个 TableView 类。我想在不先按 Enter 或双击它的情况下使单元格可编辑。我想知道是否可以在不先按 Enter 的情况下开始输入新值?谢谢。

【问题讨论】:

    标签: javafx tableview cell


    【解决方案1】:

    对于 TableView(此处命名为 tv)我这样做

       tv.setOnKeyReleased((KeyEvent t) -> {
            TablePosition tp;
            switch (t.getCode()) {
                //other code cut out here
                case Z:
                    if (t.isControlDown()) {
                        if (!deletedLines.isEmpty()) {
                            items.add(deletedLines.pop());
                        }
                        break; //don't break for regular Z
                    }
                default: 
                    if (t.getCode().isLetterKey() || t.getCode().isDigitKey()) {
                        lastKey = t.getText();
                        tp = tv.getFocusModel().getFocusedCell();
                        tv.edit(tp.getRow(), tp.getTableColumn());
                        lastKey = null;
                    }
            }
        });
    

    然后当我制作 TextField 编辑单元格时

    @Override
        public void startEdit() {
            if (!isEmpty()) {
                super.startEdit();
                createTextField();
                setText(null);
                setGraphic(textField);
                Platform.runLater(() -> {//without this space erases text, f2 doesn't
                    textField.requestFocus();//also selects
                });
                if (lastKey != null) {
                    textField.setText(lastKey);
                    Platform.runLater(() -> {
                        textField.deselect();
                        textField.end();
                        textField.positionCaret(textField.getLength()+2);//works sometimes
                    });
                }
            }
        }
    

    有时闪烁的光标会出现在 lastKey 的前面,但是当我继续输入字符时,字符会出现在末尾并且光标会移动到正确的位置。如果您键入得非常快,则第二个字符将被忽略。

    如果你能做得更好,请告诉我。我希望它更像excel。我还将它添加到标准 textField 代码中。

        textField.setOnKeyReleased((KeyEvent t) -> {
            if (t.getCode() == KeyCode.ENTER) {
                commitEdit(textField.getText());
                EditingCell.this.getTableView().requestFocus();//why does it lose focus??
                EditingCell.this.getTableView().getSelectionModel().selectBelowCell();
            } else if (t.getCode() == KeyCode.ESCAPE) {
                cancelEdit();
            }
        });
    

    【讨论】:

    • 感谢您的意见,布赖恩。一个更正:行 setContentDisplay(ContentDisplay.GRAPHIC_ONLY);应该在 setGraphic (textField) 之后插入; ,否则单元格中不会显示任何数据。我仍然缺少第一个输入的符号。
    • 如果我这样做了,那么我必须在编辑后的某个时间重新设置它。但是在我的代码中不需要它,我看到 textField 很好。我总是得到第一个符号,这是我有时会错过的第二个符号。
    • 我刚刚将开始编辑移至 onKeyPressed 并且一切正常。稍后我会尝试一个 excel 样式表并发布 ssce
    【解决方案2】:

    看来我找到了解决丢失第一个输入符号问题的方法。只要单元格处于焦点,就可以将数据输入到单元格中。输入数据前无需先回车或双击单元格。

    类 CellField

    //Text box cell
    public class CellField {
        private static StringBuffer text = new StringBuffer("");
        public static String getText() {
            return text.toString();
        }
        public static void setText(String text) {
            CellField.text = new StringBuffer(text);
        }
        //true, if the length of more than one character
        public static boolean isLessOrEqualOneSym(){
            return CellField.text.length() <= 1;
        }
        //add character to the end of line
        public static void addSymbol(String symbol){
            text.append(symbol);
        }
        public static void clearText() { 
            setText("");
        }
    }
    

    类 NewOrderCtrl(部分代码)

    class public class NewOrderCtrl extends HBox implements Initializable {
        @FXML private TableView<OrderItem> catalogTable;
        @FXML private TableColumn<OrderItem, String> numCatalogColumn;
        public void initialize(URL url, ResourceBundle resourceBundle) {
                numCatalogColumn.setCellFactory(new Callback<TableColumn<OrderItem, String>, TableCell<OrderItem, String>>() {
                @Override
                public TableCell<OrderItem, String> call(TableColumn<OrderItem, String> orderItemStringTableColumn) {
                    return new EditingCell();
                }
            });
                catalogTable.setOnKeyPressed(new EventHandler<KeyEvent>() {
                @Override
                public void handle(KeyEvent keyEvent) {
                    KeyCode keyCode = keyEvent.getCode();
                    if (keyCode == KeyCode.ENTER || keyCode == KeyCode.ESCAPE){
                        CellField.clearText();
                    }
                    if (keyCode.isDigitKey()) {
                        int row = catalogTable.getSelectionModel().getSelectedIndex();
                        catalogTable.edit(row, numCatalogColumn);
                    }
                }
            });
        }
    
        @FXML
        private void onEditStart() {
            CellField.clearText();
        }
    }
    

    类编辑单元

    public class EditingCell extends TableCell<OrderItem, String> {
        private TextField textField;
        @Override
        public void startEdit() {
            if (!isEmpty()) {
                super.startEdit();
                if (textField == null) {
                    createTextField();
                }
                setText(null);
                setGraphic(textField);
                setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                textField.requestFocus();
            }
        }
    
        @Override
        public void cancelEdit() {
            super.cancelEdit();
            setText(String.valueOf(getItem()));
            setContentDisplay(ContentDisplay.TEXT_ONLY);
        }
    
        @Override
        public void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);
            if (empty) {
                setText(null);
                setGraphic(null);
            } else {
                if (isEditing()) {
                    if (textField != null) {
                        textField.setText(getString());
                    }
                    setGraphic(textField);
                    setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                } else {
                    setText(getString());
                    setContentDisplay(ContentDisplay.TEXT_ONLY);
                }
            }
        }
    
        private void createTextField() {
            textField = new TextField(getString());
            textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
            textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
                @Override
                public void handle(KeyEvent t) {
                    if (t.getCode() == KeyCode.ENTER) {
                        commitEdit(textField.getText());
                        EditingCell.this.getTableView().requestFocus();//why does it lose focus??
                        EditingCell.this.getTableView().getSelectionModel().selectBelowCell();
                    } else if (t.getCode() == KeyCode.ESCAPE) {
                        cancelEdit();
                    }
                }
            });
    
            textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
                @Override
                public void handle(KeyEvent t) {
                    if (t.getCode().isDigitKey()) {
                        if (CellField.isLessOrEqualOneSym()) {
                            CellField.addSymbol(t.getText());
                        } else {
                            CellField.setText(textField.getText());
                        }
                        textField.setText(CellField.getText());
                        textField.deselect();
                        textField.end();
                        textField.positionCaret(textField.getLength() + 2);//works sometimes
    
                    }
                }
            });
        }
    
        private String getString() {
            return getItem() == null ? "" : getItem();
        }
    }
    

    【讨论】:

      【解决方案3】:

      我终于让一切都按我喜欢的方式工作了。我添加了一些格式化的东西,因为我需要测试它。用户必须输入一些数据,越接近 excel 越容易被大多数人使用。

      在easyedit包中创建一个名为TableTest的新javaFX项目,并将这些文件粘贴到正确的类名中。

      TableTest.java

      package easyedit;
      
      import javafx.application.Application;
      import javafx.collections.FXCollections;
      import javafx.collections.ObservableList;
      import javafx.scene.Scene;
      import javafx.scene.control.Button;
      import javafx.scene.control.TableView;
      import javafx.scene.layout.VBox;
      import javafx.stage.Stage;
      
      public class TableTest extends Application {
      
          @Override
          public void start(Stage primaryStage) {
              ObservableList<LineItem> items = FXCollections.observableArrayList();
              items.addAll(new LineItem("hello",123.45,6),
                           new LineItem("world",0.01,11));
              TableView table = new EasyEditTable().makeTable(items);
      
              Button focusableNode = new Button("Nada");
      
              VBox root = new VBox();
              root.getChildren().addAll(table, focusableNode);
              Scene scene = new Scene(root, 300, 250);
              //css to remove empty lines in table
              scene.getStylesheets().add(this.getClass().getResource("css.css").toExternalForm());
      
              primaryStage.setTitle("Easy edit table test");
              primaryStage.setScene(scene);
              primaryStage.show();
          }
      
      }
      

      LineItem.java

      package easyedit;
      
      import javafx.beans.property.DoubleProperty;
      import javafx.beans.property.IntegerProperty;
      import javafx.beans.property.SimpleDoubleProperty;
      import javafx.beans.property.SimpleIntegerProperty;
      import javafx.beans.property.SimpleStringProperty;
      import javafx.beans.property.StringProperty;
      
      public class LineItem {
      
              private final StringProperty desc = new SimpleStringProperty();
              private final DoubleProperty amount = new SimpleDoubleProperty();
              private final IntegerProperty sort = new SimpleIntegerProperty();
      
              public StringProperty descProperty() {return desc;}
              public DoubleProperty amountProperty() {return amount;}
              public IntegerProperty sortProperty() {return sort;}
      
              public LineItem(String dsc, double amt, int srt) {
                  desc.set(dsc); amount.set(amt); sort.set(srt);
              }
      }
      

      EasyEditTable.java

      package easyedit;
      
      import java.text.NumberFormat;
      import java.util.Stack;
      import javafx.application.Platform;
      import javafx.beans.property.SimpleStringProperty;
      import javafx.beans.value.ObservableValue;
      import javafx.collections.ObservableList;
      import javafx.geometry.Pos;
      import javafx.scene.control.SelectionMode;
      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.control.cell.PropertyValueFactory;
      import javafx.scene.input.KeyCode;
      import javafx.scene.input.KeyEvent;
      import javafx.util.Callback;
      
      
      public class EasyEditTable{
          private String lastKey = null;
      
          public TableView makeTable(ObservableList<LineItem> items) {
              TableView tv = new TableView(items);
              tv.setEditable(true);
      
              Stack<LineItem> deletedLines = new Stack<>();
              tv.setUserData(deletedLines);
              Callback<TableColumn<LineItem,String>, TableCell<LineItem,String>> txtCellFactory = 
                      (TableColumn<LineItem,String> p) -> {return new EditingCell();};
      
              TableColumn<LineItem,String> descCol = new TableColumn<>("desc");
              descCol.setCellValueFactory(new PropertyValueFactory<>("desc"));
              descCol.setCellFactory(txtCellFactory);
              descCol.setOnEditCommit((TableColumn.CellEditEvent<LineItem, String> evt) -> {
                  evt.getTableView().getItems().get(evt.getTablePosition().getRow())
                          .descProperty().setValue(evt.getNewValue());
              });
      
      
              final NumberFormat currFmt = NumberFormat.getCurrencyInstance();
              TableColumn<LineItem, String> amountCol = new TableColumn<>("amount");
              amountCol.setCellValueFactory((TableColumn.CellDataFeatures<LineItem, String> p) -> {
                      return new SimpleStringProperty(currFmt.format(p.getValue().amountProperty().get()));
              });
              amountCol.setCellFactory(txtCellFactory);
              amountCol.setOnEditCommit((TableColumn.CellEditEvent<LineItem, String> evt) -> {
                  try {
                    evt.getTableView().getItems().get(evt.getTablePosition().getRow())
                              .amountProperty().setValue(Double.parseDouble(evt.getNewValue().replace("$","")));
                  } catch (NumberFormatException nfe) {
                      //handle error properly somehow
                  }
              });
              amountCol.setComparator((String o1, String o2) -> {
                  try {//only works in $ countries, use currFmt.parse() instead
                      return Double.compare(Double.parseDouble(o1.replace("$", "")),
                                            Double.parseDouble(o2.replace("$", "")));
                  } catch (NumberFormatException numberFormatException) {
                      return 0;
                  }
              });
      
              TableColumn<LineItem,String> sortCol = new TableColumn<>("sort");
              sortCol.setCellValueFactory(new PropertyValueFactory("sort"));
              sortCol.setCellFactory(txtCellFactory);
              sortCol.setOnEditCommit((TableColumn.CellEditEvent<LineItem, String> evt) -> {
                  evt.getTableView().getItems().get(evt.getTablePosition().getRow())
                          .sortProperty().setValue(Integer.parseInt(evt.getNewValue()));//throws nfe
              });
      
              tv.getColumns().setAll(descCol, amountCol, sortCol);
              tv.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
              tv.getSelectionModel().setCellSelectionEnabled(true);
              tv.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
      
              tv.addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent t) -> {
                  if (tv.getEditingCell() == null && t.getCode() == KeyCode.ENTER) {
                      if (t.isShiftDown()) {
                          tv.getSelectionModel().selectAboveCell();
                      } else {
                          tv.getSelectionModel().selectBelowCell();
                      }
                      t.consume();
                  }
                  //I decided not to override the default tab behavior
                  //using ctrl tab for cell traversal, but arrow keys are better
                  if (t.isControlDown() && t.getCode() == KeyCode.TAB) {
                      if (t.isShiftDown()) {
                          tv.getSelectionModel().selectLeftCell();
                      } else {
                          tv.getSelectionModel().selectRightCell();
                      }
                      t.consume();
                  }
              });
      
              tv.setOnKeyPressed((KeyEvent t) -> {
                  TablePosition tp;
                  if (!t.isControlDown() && 
                     (t.getCode().isLetterKey() || t.getCode().isDigitKey())) {
                      lastKey = t.getText();
                      tp = tv.getFocusModel().getFocusedCell();
                      tv.edit(tp.getRow(),tp.getTableColumn());
                      lastKey = null;
                  }
              });
      
              tv.setOnKeyReleased((KeyEvent t) -> {
                  TablePosition tp;
                  switch (t.getCode()) {
                      case INSERT:
                          items.add(new LineItem("",0d,0));//maybe try adding at position
                          break;
                      case DELETE:
                          tp = tv.getFocusModel().getFocusedCell();
                          if (tp.getTableColumn() == descCol) {
                              deletedLines.push(items.remove(tp.getRow()));
                          } else { //maybe delete cell value
                          }
                          break;
                      case Z:
                          if (t.isControlDown()) {
                              if (!deletedLines.isEmpty()) {
                                  items.add(deletedLines.pop());
                              }
                          }
                  }
              });
      
              return tv;
          }
      
          private class EditingCell extends TableCell{
      
              private TextField textField;
      
              @Override
              public void startEdit() {
                  if (!isEmpty()) {
                      super.startEdit();
                      createTextField();
                      setText(null);
                      setGraphic(textField);
                      //setContentDisplay(ContentDisplay.GRAPHIC_ONLY); 
                      Platform.runLater(() -> {//without this space erases text, f2 doesn't
                          textField.requestFocus();//also selects
                      });
                      if (lastKey != null) {
                          textField.setText(lastKey);
                          Platform.runLater(() -> {
                              textField.deselect();
                              textField.end();
                          });
                      }
                  }
              }
      
              public void commit(){
                  commitEdit(textField.getText());
              }
      
              @Override
              public void cancelEdit() {
                  super.cancelEdit();
                  try {
                      setText(getItem().toString());
                  } catch (Exception e) {}
                  setGraphic(null);
              }
      
              @Override
              public void updateItem(Object item, boolean empty) {
                  super.updateItem(item, empty);
      
                  if (empty) {
                      setText(null);
                      setGraphic(null);
                  } else if (isEditing()) {
                      if (textField != null) {
                          textField.setText(getString());
                      }
                      setText(null);
                      setGraphic(textField);
                  } else {
                      setText(getString());
                      setGraphic(null);
                      if (getTableColumn().getText().equals("amount"))
                      setAlignment(Pos.CENTER_RIGHT);
                  }
              } 
      
              private void createTextField() {
                  textField = new TextField(getString());
      
                  //doesn't work if clicking a different cell, only focusing out of table
                  textField.focusedProperty().addListener(
                          (ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) -> {
                      if (!arg2) commitEdit(textField.getText());
                  });
      
                  textField.setOnKeyReleased((KeyEvent t) -> {
                      if (t.getCode() == KeyCode.ENTER) {
                          commitEdit(textField.getText());
                          EditingCell.this.getTableView().getSelectionModel().selectBelowCell();
                      }
                      if (t.getCode() == KeyCode.ESCAPE) {
                          cancelEdit();
                      }
                  });
      
                  textField.addEventFilter(KeyEvent.KEY_RELEASED, (KeyEvent t) -> {
                      if (t.getCode() == KeyCode.DELETE) {
                          t.consume();//stop from deleting line in table keyevent
                      }
                  });
              }
      
              private String getString() {
                  return getItem() == null ? "" : getItem().toString();
              }
          }
      
      }
      

      如果你想使用 css.css 文件。它在同一个包中。

      .table-row-cell:empty {
          -fx-background-color: ivory;
      }
      
      .table-row-cell:empty .table-cell {
          -fx-border-width: 0px;
      }
      

      我对不显示字符或空白单元格没有任何问题。只是闪烁的光标有时位于错误的位置。在 XP sp3 上使用 8-b127。我不喜欢 textField focusListener 不能很好地工作,但这是一个小问题。

      【讨论】:

        猜你喜欢
        • 2013-10-20
        • 2014-02-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-01
        • 2014-11-20
        相关资源
        最近更新 更多