当前,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();
}
}
答案 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();
}
}