JavaFX如何触发几何事件?

JavaFX how to trigger events on Geometries?

我正在编写一个 JavaFX 应用程序,我在其中使用 UTM32 坐标作为我的几何图形,我有一个取决于缩放级别的动态笔划宽度。问题是当我的笔划宽度以某个因素减小时,我无法触发事件。如果您使用 Intellij,您可以启动一个新的 fx 项目并复制以下代码以重现此行为:

你好-view.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER" prefHeight="248.0" prefWidth="335.0" spacing="20.0" xmlns="http://javafx.com/javafx/11.0.2"
      xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.demo.HelloController">
    <padding>
        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
    </padding>
    <Pane fx:id="mapPane" prefHeight="200.0" prefWidth="200.0">
        <children>
            <StackPane fx:id="stackPane" prefHeight="150.0" prefWidth="200.0">
            </StackPane>
        </children>
    </Pane>
</VBox>

HelloApplication.java

package com.example.demo;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
        Scene scene = new Scene(fxmlLoader.load(), 320, 240);
        stage.setTitle("Hello!");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

HelloController.java

package com.example.demo;

import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Shape;

import java.net.URL;
import java.util.Random;
import java.util.ResourceBundle;

public class HelloController implements Initializable {
    private final Group mapGroup = new Group();
    @FXML
    private StackPane stackPane = new StackPane();
    @FXML
    public Pane mapPane;
    double mouseAnchorX, mouseAnchorY, translateAnchorX, translateAnchorY;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        addLines();
        stackPane.getChildren().add(mapGroup);
        mapPane.setOnScroll(onMouseScrolledEventHandler());
        mapPane.setOnMousePressed(onMousePressedEventHandler());
    }

    private void addLines() {
        for (int i = 0; i < 10; ++i) {
            Line line = new Line(getRandomDouble(5000000, 5000002), getRandomDouble(5000000, 5000002),
                    getRandomDouble(5000000, 5000002), getRandomDouble(5000000, 5000002));
            line.addEventHandler(MouseEvent.MOUSE_PRESSED, onShapeSelected());
            (line).setSmooth(true);
            mapGroup.getChildren().add(line);
        }
    }

    private double getRandomDouble(double rangeMin, double rangeMax) {
        Random rand = new Random();
        return rangeMin + (rangeMax - rangeMin) * rand.nextDouble();
    }

    private EventHandler<MouseEvent> onShapeSelected() {
        return mouseEvent -> {
            if (!mouseEvent.isPrimaryButtonDown()) {
                return;
            }
            System.out.println("Event triggered");
            ((Shape) mouseEvent.getSource()).setStroke(Color.RED);
        };
    }

    public void decreaseStrokeWidth(double val) {
        for (Node shape : mapGroup.getChildren()) {
            ((Shape) shape).setStrokeWidth(val);
        }
    }

    private EventHandler<MouseEvent> onMousePressedEventHandler() {
        return event -> {
            if (event.isPrimaryButtonDown()) {
                if (!event.getPickResult().getIntersectedNode().getParent().equals(mapGroup)) {
                    for (Node shape : mapGroup.getChildren()) {
                        ((Shape) shape).setStroke(Color.BLACK);
                    }
                }
            }
            if (event.isSecondaryButtonDown()) {
                mouseAnchorX = event.getSceneX();
                mouseAnchorY = event.getSceneY();
                translateAnchorX = mapGroup.getTranslateX();
                translateAnchorY = mapGroup.getTranslateY();
            }
        };
    }

    private EventHandler<ScrollEvent> onMouseScrolledEventHandler() {
        return event -> {
            double scale = mapGroup.getScaleY();
            double oldScale = scale;
            scale = event.getDeltaY() < 0 ? (scale / 1.1) : (scale * 1.1);
            double dx = (event.getSceneX() - (mapGroup.getBoundsInParent().getWidth() / 2 + mapGroup.getBoundsInParent().getMinX()));
            double dy = (event.getSceneY() - (mapGroup.getBoundsInParent().getHeight() / 2 + mapGroup.getBoundsInParent().getMinY()));
            double f = (scale / oldScale) - 1;
            mapGroup.setScaleX(scale);
            mapGroup.setScaleY(scale);
            mapGroup.setTranslateX(mapGroup.getTranslateX() - f * dx);
            mapGroup.setTranslateY(mapGroup.getTranslateY() - f * dy);
            decreaseStrokeWidth(Math.max(1 / scale, 0.2)); // Change this parameter
            event.consume();
        };
    }
}

您可以更改线条的坐标范围和笔划宽度。现在任何人都可以向我解释这个行为,并可以给我一些如何处理这个问题的建议。我真的不想为此找到解决方法,但我的猜测是它与内部插值有关。

这就是为什么我通常不直接在几何图形上使用事件处理程序的原因。相反,我向窗格添加了一个事件处理程序,当事件发生时,我手动遍历我的几何图形。特别是对于非常细的线,它们总是很难用鼠标精确点击,我测试几何图形是否与事件位置周围定义的小矩形相交。这样我什至可以毫无问题地 select 不可见线 :-) 这个过程也有助于更复杂的 selection 方案,例如,如果你想要一个所有可能的列表 select候选离子等