击中F4时禁用ComboBox下拉菜单

时间:2019-10-24 22:40:33

标签: javafx javafx-8

当前,JavaFX提供了在击中F4时下拉comboBox的功能。我们要禁用该功能并为F4处理其他功能。首先,我认为这很简单。我的想法是,我将添加一个按键事件过滤器,并在按下F4键时使用它。

但是不幸的是那没有用!经过调查,我发现 ComboBoxPopupControl 中有一部分代码可以处理键事件,该事件设置为 KeyEvent.ANY过滤器。奇怪的是,他们在显示/隐藏后消费事件。

代码部分如下:

private void handleKeyEvent(KeyEvent ke, boolean doConsume) {
        // When the user hits the enter or F4 keys, we respond before
        // ever giving the event to the TextField.
        if (ke.getCode() == KeyCode.ENTER) {
            setTextFromTextFieldIntoComboBoxValue();

            if (doConsume && comboBoxBase.getOnAction() != null) {
                ke.consume();
            } else {
                forwardToParent(ke);
            }
        } else if (ke.getCode() == KeyCode.F4) {
            if (ke.getEventType() == KeyEvent.KEY_RELEASED) {
                if (comboBoxBase.isShowing()) comboBoxBase.hide();
                else comboBoxBase.show();
            }
            ke.consume(); // we always do a consume here (otherwise unit tests fail)
        }
    }

这使我完全无助,因为现在我仅使用过滤器/处理程序就无法再控制事件链的这一部分。以下过滤器都没有帮助我停止显示下拉列表。

comboBox.addEventFilter(KeyEvent.ANY, e -> {
    if (e.getCode() == KeyCode.F4) {
        e.consume(); // Didn't stopped showing the drop down
    }
});
comboBox.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
    if (e.getCode() == KeyCode.F4) {
        e.consume(); // Didn't stopped showing the drop down
    }
});
comboBox.addEventFilter(KeyEvent.KEY_RELEASED, e -> {
    if (e.getCode() == KeyCode.F4) {
        e.consume(); // Didn't stopped showing the drop down
    }
});

我唯一可以停止它的方法是在事件的父事件上使用事件,并且不允许委派给ComboBox。但这绝对是一项开销,整个应用程序中已经有数十个ComboBox,并且还会有更多。

我的问题是: 他们为什么要实施紧密集成的功能,不允许用户禁用它?

是否可以在ComboBox级别实施任何替代方法,以在按F4时停止显示/隐藏下拉菜单。

我尝试了以下方法使其工作。但我不确定我能在多大程度上依赖基于时间轴的解决方案:(

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class ComboBoxF4_Demo extends Application {
    Timeline f4PressedTimeline = new Timeline(new KeyFrame(Duration.millis(100), e1 -> {
    }));

    @Override
    public void start(Stage stage) throws Exception {
        HBox root = new HBox();
        root.setSpacing(15);
        root.setPadding(new Insets(25));
        root.setAlignment(Pos.CENTER);
        Scene scene = new Scene(root, 600, 600);
        stage.setScene(scene);

        final ComboBox<String> comboBox = new ComboBox<String>() {
            @Override
            public void show() {
                if (f4PressedTimeline.getStatus() != Animation.Status.RUNNING) {
                    super.show();
                }
            }
        };
        comboBox.setItems(FXCollections.observableArrayList("One", "Two", "Three"));
        comboBox.addEventFilter(KeyEvent.ANY, e -> {
            if (e.getCode() == KeyCode.F4) {
                if (e.getEventType() == KeyEvent.KEY_RELEASED) {
                    f4PressedTimeline.playFromStart();
                }
            }
        });

        // NONE OF THE BELOW FILTERS WORKED :(
        /*comboBox.addEventFilter(KeyEvent.ANY, e -> {
            if (e.getCode() == KeyCode.F4) {
                e.consume(); // Didn't stopped showing the drop down
            }
        });
        comboBox.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
            if (e.getCode() == KeyCode.F4) {
                e.consume(); // Didn't stopped showing the drop down
            }
        });
        comboBox.addEventFilter(KeyEvent.KEY_RELEASED, e -> {
            if (e.getCode() == KeyCode.F4) {
                e.consume(); // Didn't stopped showing the drop down
            }
        });
        */
        root.getChildren().addAll(comboBox);
        stage.show();
    }
}

1 个答案:

答案 0 :(得分:2)

正如@kleopatra在问题注释中提到的,使用事件并不会阻止事件在同一阶段的相同“级别”内传播。换句话说,即使其中一个使用了事件,仍将通知所有在ComboBox(对于EventType及其超类型)中注册的事件过滤器。然后,还有一个问题就是更改控件的默认行为,而这些行为可能会被最终用户意外地发现或忽略。

如果您仍然想更改控件的行为,而又没有满意地使用祖先事件,则可以在自定义EventDispatcher而不是事件过滤器中拦截该事件:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class App extends Application {

  @Override
  public void start(Stage primaryStage) {
    var comboBox = new ComboBox<String>();
    for (int i = 0; i < 20; i++) {
      comboBox.getItems().add("Item #" + i);
    }
    comboBox.getSelectionModel().select(0);

    var oldDispatcher = comboBox.getEventDispatcher();
    comboBox.setEventDispatcher((event, tail) -> {
      if (event.getEventType() == KeyEvent.KEY_RELEASED
          && ((KeyEvent) event).getCode() == KeyCode.F4) {
        return null; // returning null indicates the event was consumed
      }
      return oldDispatcher.dispatchEvent(event, tail);
    });

    primaryStage.setScene(new Scene(new StackPane(comboBox), 500, 300));
    primaryStage.show();
  }

}