How to make an editable JavaFX TableView cell without first pressing Enter?

I am learning JavaFX and implementing the TableView class. I would like to make an editable cell without first pressing Enter or double-clicking on it. I wonder if it is possible to start entering a new value without first pressing Enter? Thanks.

+4
source share
3 answers

Looks like I found a solution to the problem of missing the first characters entered. Data can be entered into the cell as soon as the cell is in focus. There is no need to press Enter first or double-click on a cell before entering data.

CellField Class

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

Class NewOrderCtrl (part of the code)

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

Class editing

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

Key, , , . , .

, . , , 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();
        }
    });
0

- , . , . , , .

Create a new javaFX project called TableTest in the easyedit package and paste these files into the desired class names.

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

}

The css.css file if you want to use it. It comes in the same package.

.table-row-cell:empty {
    -fx-background-color: ivory;
}

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

I have no problem with characters that don't display or empty cells. Sometimes the blinking cursor is in the wrong place. Using 8-b127 on XP sp3. I don’t like how the textField focusListener does not work very well, but this is a small problem.

0
source

All Articles