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候选离子等
我正在编写一个 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候选离子等