JavaFX坐标不准确

z9smfwbn  于 2023-05-15  发布在  Java
关注(0)|答案(1)|浏览(147)

标题有点宽泛,但我不知道如何用更少的词来描述这个问题。
我正在用JavaFX和Scene Builder制作一个应用程序,这个应用程序的要点是有一个元素列表可供选择(无论你选择哪个元素,目前都不会改变任何东西,因为我一直在试图解决这个主要问题沿着其他一些问题),并通过按插入按钮添加到中间的主要区域,这些元素在添加后是可拖动的,这是添加后的外观:

顶部的标题不是那些元素将被移动的主要区域的一部分,所以我做了一个系统,它应该阻止可拖动窗格与其他区域的交叉。


我遇到的问题是,这个标题的坐标不与所示的矩形对齐,我的意思是,在上面的图像中,可拖动的窗格不能比那个更高,它停止了这样做,但它可以在标题实际所在的区域内自由移动:

下面是我使用的代码:
主类:(这个应用程序使用了一个Json API,我已经存储在我的项目,删除任何与它有关的东西,如Gson的东西进行测试)

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.stage.Window;

import java.awt.*;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.TreeMap;

public class SkriptDnD extends Application {
    public static TreeMap<String, JsonObject> skriptElements = new TreeMap<>();
    public static TreeMap<String, ArrayList<String>> skriptTypeElementNames;

    TreeMap<String, Color> skriptTypeColors;

    {
        try {
            skriptTypeColors = new TreeMap<>() {{
                put("Event", Color.rgb(146, 44, 255));
                put("Expression", Color.rgb(123, 250, 36));
                put("Effect", Color.rgb(36, 136, 250));
                put("Condition", Color.rgb(255, 52, 52));
                put("Type", Color.rgb(255, 184, 30));
                put("Function", Color.rgb(168, 168, 168));
                put("Section", Color.rgb(104, 204, 192));
            }};
            Gson gson = new Gson();
            Reader reader = Files.newBufferedReader(Paths.get("src/main/resources/me/jake/skriptdnd/skript.json"));
            skriptTypeElementNames = new TreeMap<>();
            JsonArray jsonArray = gson.fromJson(reader, JsonArray.class);
            for (JsonElement element : jsonArray) {
                JsonObject object = element.getAsJsonObject();
                String name = object.get("title").getAsString();
                skriptElements.put(name, object);
                String doc = object.get("syntax_type").getAsString().toLowerCase();
                if (!skriptTypeElementNames.containsKey(doc)) {
                    skriptTypeElementNames.put(doc, new ArrayList<>());
                }
                skriptTypeElementNames.get(doc).add(name);
            }
            reader.close();
        } catch (Exception exception) {
            try {
                throw exception;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public void start(Stage stage) throws IOException{
        try {
            FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Main.fxml"));
            Scene scene = new Scene(fxmlLoader.load());

            String css = getClass().getResource("SkriptDnD.css").toExternalForm();
            scene.getStylesheets().add(css);

            stage.setTitle("SkriptDnD");
            stage.setScene(scene);
            stage.setMinHeight(550);
            stage.setMinWidth(800);

            StackPane header = (StackPane) scene.lookup("#header");
            Pane sidebar = (Pane) scene.lookup("#sidebar");

            ComboBox<String> typeComboBox = (ComboBox<String>) scene.lookup("#typeComboBox");
            ObservableList<String> types = typeComboBox.getItems();
            types.add("All");
            types.addAll(skriptTypeColors.keySet());
            typeComboBox.getSelectionModel().selectFirst();

            ComboBox<String> elementComboBox = (ComboBox<String>) scene.lookup("#elementComboBox");
            elementComboBox.getItems().addAll(skriptElements.keySet());
            elementComboBox.getSelectionModel().selectFirst();

            Pane canvasPane = (Pane) scene.lookup("#canvasPane");
            canvasPane.setOnMousePressed(mouseEvent2 -> {
                Point location = MouseInfo.getPointerInfo().getLocation();
                int mouseX = location.x;
                int mouseY = location.y;
                Window window = scene.getWindow();
                int windowX = (int) window.getX();
                int windowY = (int) window.getY();
                System.out.print("\n"+new Point(mouseX-windowX,mouseY-windowY));
            });
            Button insertButton = (Button) scene.lookup("#insertButton");
            insertButton.setOnMousePressed(mouseEvent -> {
                DragPane elementPane = new DragPane(true);
                elementPane.setTranslateX(300);
                elementPane.setTranslateY(300);
                elementPane.setPrefWidth(100);
                elementPane.setPrefHeight(100);
                elementPane.addIntersects(header);
                System.out.print(Utils.getRectangle(header));
                //elementPane.addIntersects(sidebar);
                Rectangle rectangle = new Rectangle(100,100,Color.rgb(100,100,100));
                rectangle.setX(0);
                rectangle.setY(0);
                rectangle.setArcWidth(10);
                rectangle.setArcHeight(10);
                elementPane.setOnMousePressed(mouseEvent12 -> {
                    if (mouseEvent12.getButton() == MouseButton.MIDDLE) {
                        canvasPane.getChildren().remove(elementPane);
                    }
                });
                elementPane.getChildren().add(rectangle);
                canvasPane.getChildren().add(elementPane);

            });
            stage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

DragPane类:

import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.stage.Window;

import java.awt.*;
import java.util.ArrayList;

public class DragPane extends Pane {
    public Point lastValid = new Point();
    public Point offset = new Point();
    public ArrayList<Region> intersects = new ArrayList<>();

    public DragPane(boolean draggable) {
        if (draggable) {
            addEventFilter(
                    MouseEvent.MOUSE_PRESSED,
                    mouseEvent -> {
                        Point location = MouseInfo.getPointerInfo().getLocation();
                        offset.setLocation(location.x, location.y);
                    }
            );

            setOnMouseDragged(mouseEvent -> {
                Node source = (Node) mouseEvent.getSource();
                Scene scene = source.getScene();
                Window window = scene.getWindow();
                int windowX = (int) window.getX();
                int windowY = (int) window.getY();
                int windowWidth = (int) window.getWidth();
                int windowHeight = (int) window.getHeight();
                Rectangle windowRectangle = new Rectangle(windowX, windowY, windowWidth, windowHeight);
                Point location = MouseInfo.getPointerInfo().getLocation();
                int mouseX = location.x;
                int mouseY = location.y;
                Point futurePoint = new Point(mouseX - offset.x, mouseY - offset.y);
                if (windowRectangle.contains(location)) {
                    if (Utils.intersects(Utils.offset(this, futurePoint),intersects)) {
                        translate(futurePoint);
                        offset.setLocation(mouseX, mouseY);
                        lastValid.setLocation(futurePoint);
                    } else {
                        Point lastValidXPoint = new Point(futurePoint);
                        lastValidXPoint.x = lastValid.x;
                        Point lastValidYPoint = new Point(futurePoint);
                        lastValidYPoint.y = lastValid.y;
                        if (Utils.intersects(Utils.offset(this, lastValidXPoint),intersects)) {
                            translate(lastValidXPoint);
                        } else if (Utils.intersects(Utils.offset(this, lastValidYPoint),intersects)) {
                            translate(lastValidYPoint);
                        }

                    }
                }
            });
        }
    }

    public void translate(Point point) {
        setTranslateX(getTranslateX() + point.x);
        setTranslateY(getTranslateY() + point.y);
    }

    public void addIntersects(Region region) {
        intersects.add(region);
    }

    public void clearIntersects() {
        intersects.clear();
    }

}

实用程序类:

import javafx.scene.layout.Region;

import java.awt.*;
import java.util.ArrayList;

public class Utils {
    public static Rectangle getRectangle(Region region) {
        return new Rectangle((int) region.getLayoutX(), (int) region.getLayoutY(), (int) region.getWidth(), (int) region.getHeight());
    }

    public static Rectangle offset(Region region, Point offset) {
        double x = region.getTranslateX()+offset.x;
        double y = region.getTranslateY()+offset.y;
        double w = region.getWidth();
        double h = region.getHeight();
        return new Rectangle((int) x, (int) y, (int) w, (int) h);
    }

    public static boolean intersects(Rectangle rectangle, ArrayList<Region> intersects) {
        return intersects.stream().noneMatch((other) -> Utils.getRectangle(other).intersects(rectangle));
    }

}

控制器类别:

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;

import java.util.Collections;
import java.util.Objects;

public class Controller {

    public void typeSelect(ActionEvent event) {
        Node source = (Node) event.getSource();
        Scene scene = source.getScene();
        ComboBox<String> typeComboBox = (ComboBox<String>) scene.lookup("#typeComboBox");
        ComboBox<String> elementComboBox = (ComboBox<String>) scene.lookup("#elementComboBox");
        String selected = typeComboBox.getSelectionModel().getSelectedItem().toLowerCase();
        ObservableList<String> list = FXCollections.observableArrayList();
        if (Objects.equals(selected, "all")) {
            list.setAll(SkriptDnD.skriptElements.keySet().stream().toList());
        } else {
            list.setAll(SkriptDnD.skriptTypeElementNames.get(selected));
        }
        Collections.sort(list);
        elementComboBox.getItems().setAll(list);
        elementComboBox.getSelectionModel().selectFirst();
    }

    public void elementSelect(ActionEvent event) {

    }
}

FXML文件:

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

<?import javafx.geometry.Insets?>
<?import javafx.geometry.Point3D?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ToggleButton?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.text.Font?>
<?import org.kordamp.ikonli.javafx.FontIcon?>

<AnchorPane fx:id="anchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="550.0" minWidth="800.0" prefHeight="550.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="me.jake.skriptdnd.Controller">
   <children>
      <BorderPane prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
         <top>
            <StackPane fx:id="header" alignment="CENTER_RIGHT" prefHeight="59.0" prefWidth="800.0" BorderPane.alignment="CENTER">
               <children>
                  <Pane prefHeight="59.0" prefWidth="538.0" style="-fx-background-color: #2c2b2b;">
                     <rotationAxis>
                        <Point3D />
                     </rotationAxis></Pane>
                  <Label fx:id="label1" text="SkriptDnD" textAlignment="CENTER" textFill="WHITE" wrapText="true" StackPane.alignment="CENTER_LEFT">
                     <font>
                        <Font name="Arial Bold" size="18.0" />
                     </font>
                     <StackPane.margin>
                        <Insets left="40.0" />
                     </StackPane.margin>
                  </Label>
                  <HBox alignment="CENTER_RIGHT" nodeOrientation="LEFT_TO_RIGHT">
                     <children>
                        <Button fx:id="insertButton" mnemonicParsing="false" text="Insert" textOverrun="CLIP">
                           <HBox.margin>
                              <Insets right="20.0" />
                           </HBox.margin>
                           <font>
                              <Font name="Arial Bold" size="12.0" />
                           </font>
                        </Button>
                        <ComboBox fx:id="typeComboBox" nodeOrientation="LEFT_TO_RIGHT" onAction="#typeSelect" prefHeight="17.0" prefWidth="150.0" promptText="Type" visibleRowCount="7" HBox.hgrow="ALWAYS">
                           <opaqueInsets>
                              <Insets />
                           </opaqueInsets>
                           <HBox.margin>
                              <Insets right="20.0" />
                           </HBox.margin>
                        </ComboBox>
                        <ComboBox fx:id="elementComboBox" onAction="#elementSelect" prefHeight="17.0" prefWidth="150.0" promptText="Element" HBox.hgrow="ALWAYS" />
                     </children>
                     <opaqueInsets>
                        <Insets />
                     </opaqueInsets>
                     <padding>
                        <Insets right="50.0" />
                     </padding>
                  </HBox>
               </children>
            </StackPane>
         </top>
         <left>
            <StackPane prefHeight="491.0" prefWidth="40.0" BorderPane.alignment="CENTER">
               <children>
                  <Pane fx:id="sidebar" prefHeight="491.0" prefWidth="34.0" style="-fx-background-color: #ebebeb; -fx-border-color: #cfcece; -fx-border-style: solid; -fx-border-width: 0 1px 0 0;">
                     <children>
                        <ToggleButton layoutX="-3.0" mnemonicParsing="false" prefHeight="38.0" prefWidth="2.0" style="-fx-background-color: #ebebeb;" />
                        <FontIcon iconLiteral="far-arrow-alt-circle-right" iconSize="30" layoutX="5.0" layoutY="30.0" />
                     </children>
                  </Pane>
               </children>
            </StackPane>
         </left>
         <center>
            <Pane fx:id="canvasPane" BorderPane.alignment="CENTER" />
         </center>
      </BorderPane>
   </children>
</AnchorPane>

场景生成器层次+预览:

qni6mghb

qni6mghb1#

问题是您使用Windows来计算位置。在计算时,您需要始终使用相同的X,Y,例如,如果您希望基于场景进行计算

import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.stage.Window;

import java.awt.*;
import java.util.ArrayList;

public class DragPane extends Pane {
    public Point lastValid = new Point();
    public Point current = new Point();
    public ArrayList<Region> intersects = new ArrayList<>();
    public Pane myPane;
    public DragPane(boolean draggable,Pane myPane) {//get the pane to use for calculate location
        this.myPane = myPane;
        if (draggable) {
            addEventFilter(
                    MouseEvent.MOUSE_PRESSED,
                    mouseEvent -> {
                        Bounds boundsInScene = this.localToScene(this.getBoundsInLocal());
                        current.setLocation(boundsInScene.getMinX(), boundsInScene.getMinY());//use the same place always no where the click is on the shape
                    }
            );

            setOnMouseDragged(mouseEvent -> {
                Bounds boundsInScene = myPane.localToScene(myPane.getBoundsInLocal());
                int windowX = (int) boundsInScene.getMinX();
                int windowY = (int) boundsInScene.getMinY();
                int windowWidth = (int) myPane.getWidth();
                int windowHeight = (int) myPane.getHeight();
                if(mouseEvent.getEventType() != MouseEvent.MOUSE_DRAGGED){
                    return;
                }
                int mouseX = (int)mouseEvent.getSceneX() - (int) boundsInScene.getMinX();
                int mouseY = (int)mouseEvent.getSceneY() - (int) boundsInScene.getMinY();
                System.out.println("sx: " + mouseEvent.getSceneX() + " sy: " + mouseEvent.getSceneY());
                System.out.println("x: " + mouseX + " y: " + mouseY);
                System.out.println("cx: " + current.x + " cy: " + current.y);
                int futureX =  mouseX;
                int futureY =  mouseY;
                if(futureX + getWidth()/2 < windowX || futureX > windowWidth){
                    futureX = 0; //todo: take care of futureX > windowWidth
                }
                if(futureY + getHeight()/2 < windowY || futureY > windowHeight){
                    futureY = 0; //todo: take care of futureY > windowHeight
                }
                Point futurePoint = new Point(futureX, futureY);
                translate(futurePoint);
                current.setLocation(futureX, futureY);
            });
        }
    }

    public void translate(Point point) {
        setTranslateX(point.x); //this is by Scene - 0 is in myPane.boundsInScene.getMinX()
        setTranslateY(point.y); //this is by Scene - 0 is in myPane.boundsInScene.getMinY()
    }

    public void addIntersects(Region region) {
        intersects.add(region);
    }

    public void clearIntersects() {
        intersects.clear();
    }

}

相关问题