【问题标题】:How to determine if the user clicked outside a particular JavaFX node?如何确定用户是否在特定 JavaFX 节点之外单击?
【发布时间】:2016-07-01 03:07:48
【问题描述】:

我有一个TextField,如果用户点击不是TextField 本身的任何地方,我想做点什么。

  • 显然,如果您不单击节点本身,onMouseClicked 事件将不会触发,因此这是行不通的。
  • 收听focusedProperty 可能是个好主意,但问题是我的整个应用程序几乎都不是焦点可遍历的,因此在许多情况下单击文本字段外部不会取消焦点,因此侦听器不会不会被通知。

我唯一想到的就是在场景本身上放置一个事件过滤器,拦截鼠标点击,获取点击坐标并确定它们是否落在文本字段的范围内。我觉得这有点矫枉过正,我可能遗漏了一些更明显的东西。

还有其他方法可以确定用户是否点击了我的TextField 节点之外的任何地方?

【问题讨论】:

    标签: java javafx


    【解决方案1】:

    我唯一想到的就是在场景本身上放置一个事件过滤器,拦截鼠标点击,获取点击坐标并确定它们是否落在文本字段的范围内。我觉得这有点矫枉过正,我可能遗漏了一些更明显的东西。

    事实上,我认为这是您的最佳选择,但有一些变化:

    使用the PickResult provided by the MouseEvent 获取目标Node 并检查它是否在Node 的层次结构中。 (这可能是必要的,如果 Node 有子元素;例如,TextField 也包含 Text 元素。)

    事实上,这似乎是唯一无法通过在场景图中某处使用事件来破坏的选项。

    示例

    这会将焦点移至根窗格,以防用户单击TextField 以外的某个位置。

    public static boolean inHierarchy(Node node, Node potentialHierarchyElement) {
        if (potentialHierarchyElement == null) {
            return true;
        }
        while (node != null) {
            if (node == potentialHierarchyElement) {
                return true;
            }
            node = node.getParent();
        }
        return false;
    }
    
    @Override
    public void start(Stage primaryStage) {
        TextField textField = new TextField();
        textField.setMinSize(400, Region.USE_PREF_SIZE);
        textField.setMaxWidth(400);
        textField.setEditable(false);
        textField.textProperty().bind(Bindings.when(textField.focusedProperty()).then("Got the Focus!").otherwise("Please give me the focus!"));
    
        StackPane root = new StackPane();
        root.getChildren().add(textField);
    
        Scene scene = new Scene(root, 500, 200);
    
        scene.addEventFilter(MouseEvent.MOUSE_CLICKED, evt -> {
            if (!inHierarchy(evt.getPickResult().getIntersectedNode(), textField)) {
                root.requestFocus();
            }
        });
    
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    

    【讨论】:

      【解决方案2】:

      假设您的应用的顶级容器是Pane。您可以将鼠标单击事件应用于该窗格,并在其中处理文本字段的鼠标单击事件。因此,如果用户点击textfield 或点击其他地方,这两个事件都会被通知。这是示例代码。

      Pane.setOnMouseClicked((MouseEvent evt) -> {
          System.out.println("Click outside textfield");
      
          textField.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
      
              @Override
              public void handle(MouseEvent event) {
                  System.out.println("textfield clicked");
              }
          });
      });
      

      如果您在该Pane 中有任何其他控件。此鼠标单击不会干扰它们,仅当您直接单击窗格时才会调用它。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-09-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-19
        相关资源
        最近更新 更多