【问题标题】:JavaFX how to trigger events on Geometries?JavaFX如何触发几何上的事件?
【发布时间】:2022-01-16 19:33:36
【问题描述】:

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

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

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

【问题讨论】:

  • 为了让这个mre有用,我相信@FXML private StackPane stackPane = new StackPane();应该是@FXML private StackPane stackPane;

标签: java javafx geometry shapes


【解决方案1】:

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

【讨论】:

  • 这也是我想过的一种方法,我希望有一种更简单的方法来解决这个问题。但是,当然,我可以在预定义的边界区域中选择最近的几何图形。复杂性也应该没问题,因为它是 O(n),即使我需要在我的情况下显示很多几何图形。无论如何,也许其他人可以解释这一点或有更好的方法。还是谢谢
  • 在最简单的情况下,这只是循环您的几何图形并测试它们是否与单击鼠标位置周围的 10x10 像素大矩形相交。 JavaFX 几何已经提供了执行此测试的方法。稍后可以细化该概念,以首先针对边界框进行测试,甚至使用空间索引。
  • 我会尝试这种方法,这似乎是目前最好的解决方法,应该不难实施。我会告诉你的
  • 工作得很好,我实现了一个算法,计算鼠标点击和相关形状边界之间的最短距离。感谢您的建议
猜你喜欢
  • 1970-01-01
  • 2019-09-10
  • 1970-01-01
  • 2018-10-17
  • 2013-12-15
  • 2017-03-26
  • 1970-01-01
  • 2018-01-07
  • 1970-01-01
相关资源
最近更新 更多