【问题标题】:How to add keyboard edit support to ComboBoxTableCell如何向 ComboBoxTableCell 添加键盘编辑支持
【发布时间】:2019-12-24 22:05:15
【问题描述】:

ComboBoxTableCell 允许在编辑模式下将ComboBox 添加到TableCell。如果调用了comboBox.show()(例如显示弹出窗口),comboBox 会按预期对按下的箭头键作出反应,并在按下回车后立即结束编辑模式。我想只使用键盘来控制编辑。我找不到使用键盘调用“comboBox.show()”的方法。

到目前为止,我一直尝试使用setOnKeyPressedComboBoxTableCell(通过工厂方法创建期间)或ComboBox(通过使用ComboBoxTableCell.getGraphic())添加回调。回调调用ComboBox.show() 来打开弹出窗口,但它们没有被调用(通过在回调中打印到System.out 进行验证)。

actColumn.setCellFactory(
        new Callback<TableColumn<S,Object>, TableCell<S,Object>>() {
    private ObservableList<Object> list=optionList;

    @SuppressWarnings("unchecked")
    @Override
    public TableCell<S, Object> call(TableColumn<S, Object> param) {
        final ComboBoxTableCell<S,Object> cell=
                new ComboBoxTableCell<S,Object>(list);
        cell.setConverter((StringConverter<Object>) converter);
        cell.setOnKeyPressed(event -> {
            cell.startEdit();

            Node node=cell.getGraphic();
            System.out.println(node);
            if(node instanceof ComboBox) {
                System.out.println("Hit Key.");
                final ComboBox<?> box=(ComboBox<?>) node;
                box.show();
            }
        });
        //We have to forcefully fill the combobox member and set the
        //graphic, because the cell does not init the ComboBox in 
                    //its constructor
        Platform.runLater(new Runnable() {
            @Override public void run() {
                cell.startEdit();

                Node node=cell.getGraphic();
                if(node instanceof ComboBox) {
                    ComboBox<?> box=(ComboBox<?>) node;
                    //Now we should have the combobox for this cell
                    box.setOnKeyPressed(event -> {
                        System.out.println("Hit Key.");
                        if(event.getCode()==KeyCode.DOWN) {
                            System.out.println("Hit Arrow.");
                            box.show();
                        }
                    });
                }

                //Stop editing again
                cell.cancelEdit();
             }
        });

        return cell;
    }
});

除了这段代码很奇怪之外,当在单元格的编辑模式下按下一个键时,处理程序不会被调用(或者至少我没有得到输出)。

我希望能够选择一个ComboBoxTableCell,按回车键(可能还有一个额外的键),然后内部ComboBox 的弹出窗口应该会在没有任何鼠标交互的情况下显示出来。

【问题讨论】:

  • 您使用的是什么版本的 JavaFX?
  • @Slaw 嗨,版本 11.0.2。
  • (您删除了对我的回答的评论,这是我的回复): 按 ENTER 不提交值的问题实际上是我回答中的大部分代码解决。我考虑使用onHiding/onHidden 处理程序,但认为语义没有意义;如果用户点击离开ComboBox 会发生什么?我相信这会导致在实际上应该取消编辑时提交编辑(尽管我没有尝试过)。
  • @Slaw 我删除了它,因为我遇到了你刚才提到的问题。
  • 应该被报告为错误,不是吗? bugs.openjdk.java.net

标签: java javafx combobox tableview


【解决方案1】:

你可以继承ComboBoxTableCell 来添加你想要的行为。这是一个概念验证

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.util.StringConverter;

public class AutoShowComboBoxTableCell<S, T> extends ComboBoxTableCell<S, T> {

    /* 
     * May want to provide alternate constructors and static methods similar to
     * the ComboBoxTableCell class (i.e. the superclass).
     */

    private boolean enterPressed;

    public AutoShowComboBoxTableCell(StringConverter<T> converter, ObservableList<T> values) {
        super(converter, values);
        getStyleClass().add("auto-show-combo-box-table-cell");

        // Assumes TableView property is set only once (valid assumption?)
        tableViewProperty().addListener(new InvalidationListener() {
            @Override
            public void invalidated(Observable observable) {
                // Need to know if editing was started by the user pressing
                // the ENTER key (see #startEdit())
                EventHandler<KeyEvent> filter = event -> {
                    if (event.getCode() == KeyCode.ENTER) {
                        enterPressed = event.getEventType() == KeyEvent.KEY_PRESSED;
                    }
                };
                // Possible memory leak? Consider using WeakEventHandler (read docs)
                getTableView().addEventFilter(KeyEvent.KEY_PRESSED, filter);
                getTableView().addEventFilter(KeyEvent.KEY_RELEASED, filter);
                observable.removeListener(this);
            }
        });
    }

    @Override
    public void startEdit() {
        if (isEditing()) return;

        super.startEdit();
        if (isEditing()) {
            if (enterPressed) {
                // Cell was put into edit mode by the user pressing ENTER. This causes
                // problems since *releasing* ENTER while the ComboBox has the focus
                // results in the value being committed; this leads to the current value
                // being committed *immediately* after entering edit mode—not what we want. 
                // To fix that we consume the first ENTER-released event and then let all
                // subsequent events through (by removing the event filter).
                addEventFilter(KeyEvent.KEY_RELEASED, new EventHandler<>() {
                    @Override public void handle(KeyEvent event) {
                        if (event.getCode() == KeyCode.ENTER) {
                            event.consume();
                            removeEventFilter(KeyEvent.KEY_RELEASED, this);
                        }
                    }
                });
            }
            ComboBox<?> comboBox = (ComboBox<?>) getGraphic();
            comboBox.requestFocus(); // Needed to allow releasing ENTER to commit the value
            comboBox.show();
        }
    }

    @Override
    public void cancelEdit() {
        if (isEditing()) {
            super.cancelEdit();
            requestTableViewFocus();
        }
    }

    @Override
    public void commitEdit(T newValue) {
        if (isEditing()) {
            super.commitEdit(newValue);
            requestTableViewFocus();
        }
    }

    // Allows user to keep navigating the table via the keyboard
    private void requestTableViewFocus() {
        TableView<S> tableView = getTableView();
        if (tableView != null) {
            tableView.requestFocus();
        }
    }

}

注意:以上使用了实现细节的知识,包括当编辑开始时图形设置为ComboBox 以及导致编辑提交的原因。实施细节如有更改,恕不另行通知。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-17
    • 2020-04-17
    • 2019-10-04
    • 1970-01-01
    • 2017-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多