如何在不先按Enter键的情况下编辑JavaFX TableView单元格?

时间:2014-02-24 12:18:14

标签: javafx tableview cell

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

3 个答案:

答案 0 :(得分:1)

看起来我找到了丢失第一个输入符号问题的解决方案。一旦细胞聚焦,就可以将数据输入细胞。在输入数据之前,没有必要先按Enter键或双击单元格。

Class 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();
    }
}

答案 1 :(得分:0)

对于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();
        }
    });

答案 2 :(得分:0)

我终于把一切都搞定了。我添加了一些格式化的东西,因为我需要测试它。用户必须输入一些数据,越接近越好,大多数人都越容易使用。

在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不能很好地工作,但这是一个小问题。