首页 > 解决方案 > 如何将键事件分派到鼠标下方的正确 JavaFX 形状?

问题描述

我有一个Scene带有几个不同Groups 的 JavaFX。每组有几个填充PolyLine的 s。

n当用户在填充PolyLine的 s之一内按下键时,我想触发一些计算。

我曾经scene.setOnKeyPressed安装一个KeyEvent处理程序并将其打印出来。我不确定如何确定PolyLine事件发生在哪个时间点。该target事件恰好是Button我关注的早期教程的遗留问题。事件打印输出不显示任何坐标,即使有一些坐标,我也不确定如何最好地遍历Node树来寻找PolyLine感兴趣的坐标。

响应鼠标悬停在 JavaFX 上时发生的关键事件的正确习惯用法是Shape什么?

标签: javafx

解决方案


关键事件仅传递给Node具有焦点的那个。Node您可以通过调用来请求获得焦点Node#requestFocus()。使用它,一种解决方案是您可以将鼠标事件处理程序添加到每个Shape在鼠标进入该区域时请求焦点的事件处理程序。然后将关键事件处理程序添加到每个Shape而不是Scene. 键事件将被传递给Node具有焦点的 并由其处理,焦点将是Shape鼠标悬停在其上的 。一旦鼠标离开该区域,要停止将关键事件传递到Shape该区域,您可以添加一个请求焦点的处理程序(例如,公共父级或场景的根)。

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;

public final class App extends Application {

    @Override
    public void start(Stage primaryStage) {
        var root = new HBox(15, createCircle(), createTriangle(), createSquare());
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(30));

        primaryStage.setScene(new Scene(root));
        primaryStage.setTitle("Shapes");
        primaryStage.show();
    }

    private Shape createCircle() {
        var circle = new Circle(100);
        addHandlers(circle, "Circle");
        return circle;
    }

    private Shape createTriangle() {
        var polygon = new Polygon(100, 0, 200, 200, 0, 200, 100, 0);
        addHandlers(polygon, "Triangle");
        return polygon;
    }

    private Shape createSquare() {
        var rectangle = new Rectangle(200, 200);
        addHandlers(rectangle, "Square");
        return rectangle;
    }

    private void addHandlers(Shape shape, String name) {
        // Notice you have access to the specific shape in the 
        // event handlers. The source of the event will also
        // be the shape.
        shape.setOnMouseEntered(event -> {
            if (!shape.isFocused()) {
                event.consume();
                shape.requestFocus();
            }
        });
        shape.setOnMouseExited(event -> {
            if (shape.isFocused()) {
                event.consume();
                shape.getScene().getRoot().requestFocus();
            }
        });
        shape.setOnKeyPressed(event -> {
            if (event.getCode() == KeyCode.N) {
                event.consume();
                System.out.println("KEY_PRESSED['N']: " + name);
            }
        });
    }

}

添加事件处理程序的另一个MOUSE_ENTERED选择是监听. 当它变为 时,请求焦点;当它变为 时,移除焦点。MOUSE_EXITEDNode#hoverShapetruefalse


请求拥有焦点会将焦点从当前拥有焦点Shape的焦点上拉开。Node例如,如果您有TextField焦点,那么只要鼠标悬停在其中一个Shapes 上,它就会失去焦点。这将阻止用户输入TextField(直到他们去点击它)。

如果这种行为是不可取的,那么您可以维护一些字段/属性/模型来告诉您Shape当前悬停在哪个字段/属性/模型上,而不是请求焦点。然后,您将事件过滤器TextField添加到and的共同父级Shape。如果 aShape将鼠标悬停在它上面,则执行必要的操作,然后使用该事件(这将阻止它到达任何Node具有焦点的事件)。


推荐阅读