JavaFx:组合框表单元格双击 [英] JavaFx: ComboBox Table Cell double click

查看:93
本文介绍了JavaFx:组合框表单元格双击的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题如下:

我有一个TableViewComboBoxes,对于每个TableCell我都可以选择 组合框中的值.问题是,如果我有很多行和列,我必须单击很多以在每个comboBox中选择适当的值. 要在组合框中选择一个值,我必须单击四次以选择该值.一次选择单元格,一次设置comboBox的图形,再次打开combobox的弹出窗口,我可以在其中选择值,最后选择值.

I have a TableView with ComboBoxes where for every TableCell I can select a value from the comboboxes. The problem is, if I have a lot of rows and columns I have to click a lot to select the appropriate value in every comboBox. To select a value in a combobox I have to click four times to select the value. Once to select the cell, once to set the graphics the comboBox, again to open the popup for the combobox where I can select the value and finally to select the value.

我想使用doubleClick,因此我可以快速打开comboBox,然后选择值.如果我要选择的值很多,这样可以节省点击次数和很多时间.

I would like to use doubleClick, so I can open fast the comboBox, then I select the value. This would save a click and a lot of time if I have a lot of values to select.

我试图解决它,但是所有解决方案都无法正常工作,

I tried to solve it ,but none of the solutions worked correctly,

我在这里添加了它们,也许您可​​以看到我出了错的地方并进行纠正.

I add them here, maybe you can see where I went wrong and correct it.

我尝试了两种类似的方法:

I tried two similar ways:

  1. 忽略startEdit()并将鼠标单击侦听器添加到单元格,然后双击双击组合框. 如果我单击另一个单元格,即使将setGrapichs(null)都放在cancelEditcommitEdit中,前一个单元格也不会将图形设置为null,这会带来问题.另一个问题是有时不将值提交给模型.

  1. ignore startEdit() and add a mouseclick listener to the cell, and pop the comboBox on double click. This has the problem if I click another cell, the previous doesn't set the graphics to null, even if I put the setGrapichs(null) both in cancelEdit and commitEdit. Another problem is that sometimes doesn't commits the value to the model.

第二种方法是在startEdit()中对其进行处理,因此只需根据操作在其提交和取消编辑中分别调用.show().hide()即可.这给了我NPE,如果将TableView包裹在TitledPane中,然后折叠/展开后,我会尝试选择一个值,双击它会得到NPE:

The second approach was to handle it in startEdit(), so simply call the .show() there and .hide() it in both commit and cancel edit, depending on action. This gives me a NPE, if I wrap the TableView in a TitledPane and after I collapse/expand it, I try to select a value, after double click it gives 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)

这是您可以检查的代码:

Here is the code where you can check:

TableCell:

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

推荐答案

我在使用方法(2)时遇到了相同的问题,单击该按钮时我的状态处于以下情况.出现comboBox,但不会扩展.进一步单击将不会触发任何操作.

I encountered the same issue with Method (2), with mine being in the condition below when clicked. The comboBox appears, but just won't expand. Further click will not trigger anything.

最后,我在comboBox上实现了焦点侦听器,该焦点侦听器将在焦点显示时显示弹出窗口.我在startEdit的末尾打了requestFocus.

In the end, I implemented a focus listener on the comboBox which will show the popup when it has focus. I called requestFocus at the end of 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正确绘制其布局所必需的.否则,根据布局是否已更新,您将随机遇到以下情况.

The delay above is required for the comboBox to draw its layout correctly. Or else, you will end up with the following condition randomly, depending on whether the layout already updates or not.

在这么小的延迟下,您将一直得到正确的显示.

With that small delay, you will get it correct all the time.

P.S.如果您正在考虑,为什么不只是在短暂的延迟后直接调用comboBox.show()并删除focusListener?好吧,在我的测试中,当您创建新行或调用table.refresh()时,comboBox只是无法随机显示(类似于第一张图片).我想这与virtualFlow和为同一实例创建的tableCell的多个实例有关.请参见此处.

P.S. If you are thinking, why not just directly call comboBox.show() after a small delay and remove the focusListener? Well, in my test, the comboBox just fail to show randomly (similar to 1st image) when you create new row or call table.refresh(). I guess it has something to do with virtualFlow and multiple instances of tableCell being created for the same instance. See here.

这篇关于JavaFx:组合框表单元格双击的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆