Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Has anyone figured out how to make a javafx tableview act like a jtable?

I followed Example 13-11 Alternative Solution Of Cell Editing from the offical tableview tutorial, but I want my tableview to act like a jtable. This means that when a cell gets focus, it is ready to be edited, and using the arrow keys or the Enter key should instantly commit the edit and move to the next cell.

This is what I've gone so far:

First, I added

table.getSelectionModel().setCellSelectionEnabled(true);

Then I tried to modify the class EditingCell:

class EditingCell extends TableCell<Person, String> {

    private TextField textField;

    public EditingCell() {
    }

    @Override
    public void updateSelected(boolean selected) {
        super.updateSelected(selected);
        if (selected) {
            createTextField();
            setText(null);
            setGraphic(textField);
            textField.requestFocus();
            textField.selectAll();
        } else {
            String value = textField.getText();
            if (value != null) {
                commitEdit(value);
            } else {
                commitEdit(null);
            }
        }
    }


    @Override
    public void cancelEdit() {
        super.cancelEdit();

        setText((String) getItem());
        setGraphic(null);
    }

    @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());
                }
                setText(null);
                setGraphic(textField);
            } else {
                setText(getString());
                setGraphic(null);
            }
        }
    }

    private void createTextField() {
        textField = new TextField(getString());
        textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
        textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> arg0,
                    Boolean arg1, Boolean arg2) {
                if (!arg2) {
                    commitEdit(textField.getText());
                }
            }
        });
        textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent t) {
                if ((t.getCode() == KeyCode.ENTER) || (t.getCode() == KeyCode.UP) || (t.getCode() == KeyCode.DOWN) || (t.getCode() == KeyCode.LEFT) || (t.getCode() == KeyCode.RIGHT)) {
                    t.consume();
                    String value = textField.getText();
                    if (value != null) {
                        commitEdit(value);
                    } else {
                        commitEdit(null);
                    }
                } else if (t.getCode() == KeyCode.ESCAPE) {
                    cancelEdit();
                }
            }
        });
    }

    private String getString() {
        return getItem() == null ? "" : getItem().toString();
    }
}

The tableview I got is a mess - I have to click Enter key twice to end editing, and it won't commit the edit but instead cancels it.

Can anyone point me in the right direction?

like image 471
user2259890 Avatar asked Apr 09 '13 03:04

user2259890


1 Answers

Sadly that tutorial is missing some code fixes that were introduced later in the TextFieldTableCell class for the ENTER to work properly (RT-34685 - Use onAction instead of onKeyPressed and RT-28132 - Call requestFocus()).

Here is a working example, it's a reduced version of the TextFieldTableCell code which also uses Node.fireEvent() to move to the next cell after the commit (mimics a KeyPressed event):

class EditingCell extends TableCell<Person, String>
{
    private TextField   textField;

    public EditingCell()
    {
    }

    @Override
    public void startEdit()
    {
        if (!isEditable() || !getTableView().isEditable()
                || !getTableColumn().isEditable())
        {
            return;
        }
        super.startEdit();

        if (isEditing())
        {
            if (textField == null)
            {
                createTextField();
            }
            setText(null);
            setGraphic(textField);
            textField.selectAll();
            // requesting focus so that key input can immediately go into
            // the TextField (see RT-28132)
            textField.requestFocus();
        }
    }

    @Override
    public void cancelEdit()
    {
        super.cancelEdit();

        setText((String) getItem());
        setGraphic(null);
    }

    @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());
                }
                setText(null);
                setGraphic(textField);
            } else
            {
                setText(getString());
                setGraphic(null);
            }
        }
    }

    private void createTextField()
    {
        textField = new TextField(getString());
        textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
        textField.focusedProperty().addListener(new ChangeListener<Boolean>()
        {
            @Override
            public void changed(ObservableValue<? extends Boolean> arg0,
                    Boolean arg1, Boolean arg2)
            {
                if (!arg2)
                {
                    commitEdit(textField.getText());
                }
            }
        });

        // Use onAction here rather than onKeyReleased (with check for
        // Enter), as otherwise we encounter RT-34685
        textField.setOnAction(t -> {
            commitEdit(textField.getText());
            t.consume();
        });
        textField.setOnKeyReleased(t -> {
            if (t.getCode() == KeyCode.ESCAPE)
            {
                cancelEdit();
                t.consume();
            }
        });

        textField.setOnKeyPressed(new EventHandler<KeyEvent>()
        {
            @Override
            public void handle(KeyEvent t)
            {
                if ((t.getCode() == KeyCode.UP) || (t.getCode() == KeyCode.DOWN)
                        || (t.getCode() == KeyCode.LEFT)
                        || (t.getCode() == KeyCode.RIGHT))
                {
                    // Commit the current text
                    commitEdit(textField.getText());

                    // Let's move out simulating a key press in this Cell
                    KeyEvent event = new KeyEvent(t.getSource(), t.getTarget(),
                            KeyEvent.KEY_PRESSED, "", "", t.getCode(), false, false,
                            false, false);
                    EditingCell.this.fireEvent(event);
                }
            }
        });
    }

    private String getString()
    {
        return getItem() == null ? "" : getItem().toString();
    }
}

Hope this helps!

like image 148
JavierJ Avatar answered Nov 06 '22 20:11

JavierJ