问题描述
我对JavaFx ListView组件有问题。我正在VBox内部使用带有TextField和ListView的弹出窗口。当TextField处于焦点位置时,通常可以按键盘上的Esc键关闭此弹出窗口,但是当ListView项处于焦点位置时,弹出窗口保持打开状态,
最小的可复制示例:
package sample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
MenuItem rightClickItem = new MenuItem("CLICK!");
rightClickItem.setonAction(a -> showdialog());
ContextMenu menu = new ContextMenu(rightClickItem);
Label text = new Label("Right Click on me");
text.setContextMenu(menu);
StackPane root = new StackPane(text);
Scene scene = new Scene(root,300,250);
primaryStage.setTitle("RightClick MenuItem And Dialog");
primaryStage.setScene(scene);
primaryStage.show();
}
private void showdialog() {
Dialog<ButtonType> dialog = new Dialog<>();
dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
VBox vBox = new VBox();
ListView listView = new ListView();
listView.getItems().add("Item 1");
listView.getItems().add("Item 2");
vBox.getChildren().add(new TextField());
vBox.getChildren().add(listView);
vBox.addEventHandler(KeyEvent.KEY_pressed,keyEvent -> System.err.println("Key pressed: " + keyEvent.getCode()));
dialog.getDialogPane().setContent(vBox);
dialog.showAndWait();
}
public static void main(String[] args) {
launch(args);
}
}
在我看来,Esc键已在ListView中使用,这导致关闭弹出窗口时出现问题。
只需提一下,我正在使用zulu-11.0.8 JDKFx版本。
解决方法
在我看来,Esc键已在ListView中使用,这导致关闭弹出窗口时出现问题。
这确实是问题所在-所有具有通过其各自的行为添加到ESCAPE的易用键映射的控件都会发生(f.i.同样是具有TextFormatter的TextField)。
没有干净的方法来干扰它(Behavior和InputMap尚未进入公共api)。破解的方法是从Behavior的inputMap中删除KeyMapping。当心:必须允许您变脏,即使用内部api并使用反射!
步骤:
- 抓住控件的皮肤(将控件添加到场景图形后可用)
- 反思性地了解皮肤的行为
- 从行为的inputMap中删除keyMapping
示例代码段:
private void tweakInputMap(ListView listView) {
ListViewSkin<?> skin = (ListViewSkin<?>) listView.getSkin();
// use your favorite utility method to reflectively access the private field
ListViewBehavior<?> listBehavior = (ListViewBehavior<?>) FXUtils.invokeGetFieldValue(
ListViewSkin.class,skin,"behavior");
InputMap<?> map = listBehavior.getInputMap();
Optional<Mapping<?>> mapping = map.lookupMapping(new KeyBinding(KeyCode.ESCAPE));
map.getMappings().remove(mapping.get());
}
用法:
listView.skinProperty().addListener(ov -> {
tweakInputMap(listView);
});
,
为避免使用私有 API,您可以使用事件过滤器,如果 ListView
未在编辑,则复制 Escape 键事件并在父级上触发它。从那里,复制的事件可以传播到其他处理程序中,例如关闭弹出窗口。
此外,如果您需要在应用程序中的所有 ListView
上执行此操作,您可以在 ListViewSkin
的派生类中执行此操作,并将其设置为 {{1} 的 -fx-skin
} 在你的 CSS 文件中。
.list-view