【问题标题】:JavaFx 11 ListView consumes ESCAPE key pressed event even if is not in editing stateJavaFx 11 ListView 即使不处于编辑状态也会消耗 ESCAPE 按键事件
【发布时间】:2021-01-23 14:58:13
【问题描述】:

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);
   }
}

在我看来,ListView 中使用了 Esc 键,这会导致关闭弹出窗口出现问题。

顺便提一下,我使用的是zulu-11.0.8 JDKFx版本。

【问题讨论】:

  • @kleopatra 好的,我将创建最小的可重现示例,并根据您的评论提出其他建议。

标签: javafx javafx-11


【解决方案1】:

在我看来,ListView 中使用了 Esc 键,这会导致关闭弹出窗口出现问题。

这确实是问题所在 - 所有控件都有一个消耗 KeyMapping 到 ESCAPE 的控件,这些控件由它们各自的行为添加(f.i. 也适用于带有 TextFormatter 的 TextField)。

没有干净的方法可以干扰它(Behavior 和 InputMap 还没有进入公共 api)。破解的方法是从 Behavior 的 inputMap 中删除 KeyMapping。注意:必须允许你变脏,即使用内部 api 并使用反射!

步骤:

  • 抓取控件的皮肤(将控件添加到场景图后可用)
  • 反射性地访问皮肤的行为
  • 从行为的 inputMap 中移除 keyMapping

示例代码sn-p:

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);
});

【讨论】:

    【解决方案2】:

    为避免使用私有 API,您可以使用事件过滤器,如果 ListView 未编辑,则复制 Escape 键事件并在父级上触发它。从那里,复制的事件可以传播以在其他处理程序中有用,例如关闭弹出窗口。

    另外,如果您在应用程序中的所有ListViews 上都需要此功能,您可以在ListViewSkin 的派生类中执行此操作,并将其设置为您的CSS 文件中.list-view-fx-skin

          listView.addEventFilter( KeyEvent.KEY_PRESSED, keyEvent -> {
             if( keyEvent.getCode() == KeyCode.ESCAPE && !keyEvent.isAltDown() && !keyEvent.isControlDown()
                   && !keyEvent.isMetaDown() && !keyEvent.isShiftDown()
             ) {
                if( listView.getEditingIndex() == -1 ) {
                   // Not editing.
                   final Parent parent = listView.getParent();
                   parent.fireEvent( keyEvent.copyFor( parent, parent ) );
                }
                keyEvent.consume();
             }
          } );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-23
      • 1970-01-01
      相关资源
      最近更新 更多