JavaFx: двойной щелчок ячейки таблицы ComboBox - PullRequest
0 голосов
/ 21 января 2019

Проблема заключается в следующем:

У меня есть TableView с ComboBoxes, где для каждой TableCell я могу выбрать значение из выпадающих списков. Проблема в том, что, если у меня много строк и столбцов, мне приходится много щелкать, чтобы выбрать подходящее значение в каждом поле со списком. Чтобы выбрать значение в выпадающем списке, я должен щелкнуть четыре раза, чтобы выбрать значение. Один раз, чтобы выбрать ячейку, один раз, чтобы установить графику comboBox, снова, чтобы открыть всплывающее окно для комбинированного списка, где я могу выбрать значение и, наконец, выбрать значение.

Я хотел бы использовать doubleClick, чтобы я мог быстро открыть comboBox, а затем выбрать значение. Это сэкономит клик и много времени, если у меня будет много значений для выбора.

Я пытался ее решить, но ни одно из решений не работало правильно,

Я добавляю их сюда, может быть, вы можете увидеть, где я ошибся, и исправить это.

Я пробовал два аналогичных способа:

  1. игнорируйте startEdit () и добавьте прослушиватель мыши в ячейку, а затем дважды щелкните comboBox. Это имеет проблему, если я щелкаю по другой ячейке, предыдущая не устанавливает графику на ноль, даже если я поставлю setGrapichs(null) в cancelEdit и commitEdit. Другая проблема заключается в том, что иногда значение не передается в модель.

  2. Второй подход состоял в том, чтобы обработать его в startEdit (), поэтому просто вызовите .show() там и .hide() как при фиксации, так и при отмене редактирования, в зависимости от действия. Это дает мне NPE, если я обертываю TableView в TitledPane и после того, как я сверну / разверну его, я пытаюсь выбрать значение, после двойного щелчка он дает NPE:

    Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
    at com.sun.javafx.scene.control.skin.ComboBoxPopupControl.positionAndShowPopup(ComboBoxPopupControl.java:197)
    at com.sun.javafx.scene.control.skin.ComboBoxPopupControl.show(ComboBoxPopupControl.java:170)
    at com.sun.javafx.scene.control.skin.ComboBoxBaseSkin.handleControlPropertyChanged(ComboBoxBaseSkin.java:127)
    at com.sun.javafx.scene.control.skin.ComboBoxListViewSkin.handleControlPropertyChanged(ComboBoxListViewSkin.java:159)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$61(BehaviorSkinBase.java:197)
    at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(MultiplePropertyChangeListenerHandler.java:55)
    at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
    at javafx.beans.property.ReadOnlyBooleanPropertyBase.fireValueChangedEvent(ReadOnlyBooleanPropertyBase.java:72)
    at javafx.beans.property.ReadOnlyBooleanWrapper.fireValueChangedEvent(ReadOnlyBooleanWrapper.java:103)
    at javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:110)
    at javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:144)
    at javafx.scene.control.ComboBoxBase.setShowing(ComboBoxBase.java:185)
    at javafx.scene.control.ComboBoxBase.show(ComboBoxBase.java:391)
    at stackoverflow.combo.ComboTableCell.startEdit(ComboTableCell.java:47)
    at javafx.scene.control.TableCell.updateEditing(TableCell.java:556)
    at javafx.scene.control.TableCell.lambda$new$26(TableCell.java:142)
    at javafx.beans.WeakInvalidationListener.invalidated(WeakInvalidationListener.java:83)
    at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:349)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
    at javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:74)
    at javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:102)
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112)
    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:146)
    at javafx.scene.control.TableView.setEditingCell(TableView.java:1145)
    at javafx.scene.control.TableView.edit(TableView.java:1459)
    at com.sun.javafx.scene.control.behavior.TableCellBehavior.edit(TableCellBehavior.java:108)
    at com.sun.javafx.scene.control.behavior.TableCellBehavior.edit(TableCellBehavior.java:38)
    at com.sun.javafx.scene.control.behavior.CellBehaviorBase.handleClicks(CellBehaviorBase.java:271)
    at com.sun.javafx.scene.control.behavior.TableCellBehaviorBase.simpleSelect(TableCellBehaviorBase.java:218)
    at com.sun.javafx.scene.control.behavior.TableCellBehaviorBase.doSelect(TableCellBehaviorBase.java:148)
    at com.sun.javafx.scene.control.behavior.CellBehaviorBase.mouseReleased(CellBehaviorBase.java:159)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:96)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
    at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:381)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$354(GlassViewEventHandler.java:417)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:416)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
    at com.sun.glass.ui.View.notifyMouse(View.java:937)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
    at java.lang.Thread.run(Thread.java:745)
    

Вот код, который вы можете проверить:

TableCell:

public class ComboTableCell<T,S> extends TableCell<T,S> {

    private ComboBox<S> combo;

    public ComboTableCell(Collection<S> items) {
        combo = new ComboBox<>();
        combo.setItems(FXCollections.observableArrayList(items));
        combo.prefWidthProperty().bind(widthProperty());
        combo.valueProperty().addListener((observable, oldValue, newValue) -> commitEdit(newValue));
//      1. Solution with mouse event
//      this.setOnMouseClicked(event -> {
//          if(event.getClickCount() == 2){
//              combo.getSelectionModel().select(getItem());
//              setText(null);
//              setGraphic(combo);
//              if(!combo.isShowing()){
//                  combo.show();
//              }
//          }
//      });
    }

//  2. Solution with startEdit
    @Override
    public void startEdit() {
        combo.getSelectionModel().select(getItem());
        super.startEdit();
        setText(null);
        setGraphic(combo);
        if(!combo.isShowing()){
            combo.show();
        }
    }

    @Override
    protected void updateItem(S item, boolean empty) {
        super.updateItem(item, empty);
        if(empty){
            setText(null);
            setGraphic(null);
            return;
        }
        setText(getItem().toString());
        setGraphic(null);
    }

    @Override
    public void cancelEdit() {
        super.cancelEdit();
        setText(getItem().toString());
        setGraphic(null);
        if(combo.isShowing()){
            combo.hide();
        }
    }

    @Override
    public void commitEdit(S newValue) {
        super.commitEdit(newValue);
        setGraphic(null);
        setText(getItem().toString());
        if(combo.isShowing()){
            combo.hide();
        }
        setGraphic(null);
        setText(getItem().toString());
    }
}

Контроллер:

public class Controller implements Initializable {

    @FXML
    private TableView<Model> table;
    @FXML
    private TableColumn<Model,String> col;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        table.setEditable(true);

        col.setCellValueFactory(data -> data.getValue().text);
        col.setCellFactory(factory -> new ComboTableCell<>(Arrays.asList("a","b","c")));

        table.setItems(FXCollections.observableArrayList(Arrays.asList(new Model("a"),new Model("b"))));
    }

     static class Model{

        private StringProperty text;

        public Model(String text) {
            this.text = new SimpleStringProperty(text);
        }

        public String getText() {
            return text.get();
        }

        public StringProperty textProperty() {
            return text;
        }
    }

}

FXML:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TitledPane?>
<AnchorPane xmlns="http://javafx.com/javafx"
            xmlns:fx="http://javafx.com/fxml"
            fx:controller="stackoverflow.combo.Controller">
    <TitledPane text="Table">
        <TableView fx:id="table">
            <columns>
                <TableColumn fx:id="col" prefWidth="200"/>
            </columns>
        </TableView>
    </TitledPane>
</AnchorPane>

Я бы предпочел любое решение, которое дает мне ожидаемый результат, вы можете предложить мне даже другое решение, которое использует меньше обходных путей или исправление для одного из предложенных мной "решений".

JDK версия 1.8.0_121

1 Ответ

0 голосов
/ 07 апреля 2019

Я столкнулся с той же проблемой, что и метод (2), когда при щелчке мышка находилась в состоянии, указанном ниже.ComboBox появляется, но не расширяется.Дальнейшее нажатие ничего не сработает.

enter image description here

В конце я реализовал слушатель фокуса в поле со списком, который будет показывать всплывающее окно, когда оно имеет фокус.Я позвонил requestFocus в конце startEdit.

// In the creation of comboBox, add focus listener
combo.focusProperty().addListener((observable, oldValue, isFocused) -> if (isFocused) combo.show());

@Override
public void startEdit() {
    combo.getSelectionModel().select(getItem());
    super.startEdit();
    setText(null);
    setGraphic(combo);
    // Creating a JavaFX task to make a small delay and then request focus.
    // Code below is in Kotlin and TornadoFX, but you get the point.
    runAsync {
        Thread.sleep(50)
    } ui {
        combo.requestFocus()
    }
} 

Заданная выше задержка необходима для того, чтобы comboBox правильно нарисовал свой макет.В противном случае вы получите случайное условие, зависящее от того, обновляется ли уже макет.

enter image description here

С этой небольшой задержкой выбудет все время корректным.

enter image description here

PS Если вы думаете, почему бы просто не вызвать напрямую comboBox.show () после небольшой задержки иудалить focusListener?Что ж, в моем тесте comboBox просто не отображается случайным образом (как на первом изображении), когда вы создаете новую строку или вызываете table.refresh ().Я думаю, это как-то связано с virtualFlow и несколькими экземплярами tableCell, созданными для одного и того же экземпляра.Смотрите здесь .

...