Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cassandra/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
JavaFX8:在父级之间移动组件,同时保持原位_Javafx_Coordinates_Javafx 8 - Fatal编程技术网

JavaFX8:在父级之间移动组件,同时保持原位

JavaFX8:在父级之间移动组件,同时保持原位,javafx,coordinates,javafx-8,Javafx,Coordinates,Javafx 8,我需要在容器之间移动节点(大多数剪裁的图像视图)。在“父级更改”期间,我必须保持组件可视化,因此从用户的角度来看,这种重组应该是透明的 我的场景的模式如下所示: 我必须移动的节点被剪裁、转换并应用于它们的一些效果,最初位于板窗格下。办公桌的内容组被缩放、剪裁和翻译 我想把它们移到MoverLayer。它工作正常,因为移动层绑定到板: moverLayer.translateXProperty().bind(board.translateXProperty()); moverLa

我需要在容器之间移动节点(大多数剪裁的图像视图)。在“父级更改”期间,我必须保持组件可视化,因此从用户的角度来看,这种重组应该是透明的

我的场景的模式如下所示:

我必须移动的节点被剪裁、转换并应用于它们的一些效果,最初位于
窗格下。
办公桌
内容
组被缩放、剪裁和翻译

我想把它们移到MoverLayer。它工作正常,因为
移动层
绑定到

    moverLayer.translateXProperty().bind(board.translateXProperty());
    moverLayer.translateYProperty().bind(board.translateYProperty());
    moverLayer.scaleXProperty().bind(board.scaleXProperty());
    moverLayer.scaleYProperty().bind(board.scaleYProperty());
    moverLayer.layoutXProperty().bind(board.layoutXProperty());
    moverLayer.layoutYProperty().bind(board.layoutYProperty());
因此,我可以简单地在它们之间移动节点:

public void start(MouseEvent me) {
    board.getContainer().getChildren().remove(node);
    desk.getMoverLayer().getChildren().add(node);
}


public void finish(MouseEvent me) {
    desk.getMoverLayer().getChildren().remove(node);
    board.getContainer().getChildren().add(node);
}
但是,当在
托盘
内容
移动层
之间移动节点时,它开始变得复杂。我试着使用不同的坐标(本地、家长、场景、屏幕),但不知何故总是放错了位置。似乎,当
desk.contents
的比例为1.0时,它可以将
translateX
translateY
的坐标映射到屏幕坐标,切换父坐标,然后将屏幕坐标映射回本地并用作平移。但如果缩放比例不同,坐标也会不同(节点也会移动)。我还尝试递归地将坐标映射到公共父对象(
desk
),但两者都不起作用

我的问题是,计算同一点相对于不同父点的坐标的最佳实践是什么

这是MCVE代码。对不起,我无法让它变得更简单

package hu.vissy.puzzlefx.stackoverflow;

import javafx.application.Application;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class Mcve extends Application {

    // The piece to move
    private Rectangle piece;

    // The components representing the above structure
    private Pane board;
    private Group moverLayer;
    private Pane trayContents;
    private Pane desk;
    private Group deskContents;

    // The zoom scale
    private double scale = 1;

    // Drag info
    private double startDragX;
    private double startDragY;
    private Point2D dragAnchor;

    public Mcve() {
    }

    public void init(Stage primaryStage) {

        // Added only for simulation
        Button b = new Button("Zoom");
        b.setOnAction((ah) -> setScale(0.8));

        desk = new Pane();
        desk.setPrefSize(800, 600);
        desk.setBackground(new Background(new BackgroundFill(Color.LIGHTGREEN, CornerRadii.EMPTY, Insets.EMPTY)));

        deskContents = new Group();
        desk.getChildren().add(deskContents);

        board = new Pane();
        board.setPrefSize(700, 600);
        board.setBackground(new Background(new BackgroundFill(Color.LIGHTCORAL, CornerRadii.EMPTY, Insets.EMPTY)));

        // Symbolize the piece to be dragged
        piece = new Rectangle();
        piece.setTranslateX(500);
        piece.setTranslateY(50);
        piece.setWidth(50);
        piece.setHeight(50);
        piece.setFill(Color.BLACK);
        board.getChildren().add(piece);

        // Mover layer is always on top and is bound to the board (used to display the 
        // dragged items above all desk contents during dragging)
        moverLayer = new Group();
        moverLayer.translateXProperty().bind(board.translateXProperty());
        moverLayer.translateYProperty().bind(board.translateYProperty());
        moverLayer.scaleXProperty().bind(board.scaleXProperty());
        moverLayer.scaleYProperty().bind(board.scaleYProperty());
        moverLayer.layoutXProperty().bind(board.layoutXProperty());
        moverLayer.layoutYProperty().bind(board.layoutYProperty());

        board.setTranslateX(50);
        board.setTranslateY(50);

        Pane tray = new Pane();
        tray.setPrefSize(400, 400);
        tray.relocate(80, 80);

        Pane header = new Pane();
        header.setPrefHeight(30);
        header.setBackground(new Background(new BackgroundFill(Color.LIGHTSLATEGRAY, CornerRadii.EMPTY, Insets.EMPTY)));

        trayContents = new Pane();
        trayContents.setBackground(new Background(new BackgroundFill(Color.BEIGE, CornerRadii.EMPTY, Insets.EMPTY)));
        VBox layout = new VBox();
        layout.getChildren().addAll(header, trayContents);
        VBox.setVgrow(trayContents, Priority.ALWAYS);
        layout.setPrefSize(400, 400);

        tray.getChildren().add(layout);
        deskContents.getChildren().addAll(board, tray, moverLayer, b);

        Scene scene = new Scene(desk);

        // Piece is draggable
        piece.setOnMousePressed((me) -> startDrag(me));
        piece.setOnMouseDragged((me) -> doDrag(me));
        piece.setOnMouseReleased((me) -> endDrag(me));

        primaryStage.setScene(scene);
    }

    // Changing the scale
    private void setScale(double scale) {
        this.scale = scale;
        // Reseting piece position and parent if needed
        if (piece.getParent() != board) {
            piece.setTranslateX(500);
            piece.setTranslateY(50);
            trayContents.getChildren().remove(piece);
            board.getChildren().add(piece);
        }
        deskContents.setScaleX(getScale());
        deskContents.setScaleY(getScale());
    }

    private double getScale() {
        return scale;
    }

    private void startDrag(MouseEvent me) {
        // Saving drag options
        startDragX = piece.getTranslateX();
        startDragY = piece.getTranslateY();
        dragAnchor = new Point2D(me.getSceneX(), me.getSceneY());

        // Putting the item into the mover layer -- works fine with all zoom scale level
        board.getChildren().remove(piece);
        moverLayer.getChildren().add(piece);
        me.consume();
    }

    // Doing the drag
    private void doDrag(MouseEvent me) {
        double newTranslateX = startDragX + (me.getSceneX() - dragAnchor.getX()) / getScale();
        double newTranslateY = startDragY + (me.getSceneY() - dragAnchor.getY()) / getScale();

        piece.setTranslateX(newTranslateX);
        piece.setTranslateY(newTranslateY);
        me.consume();
    }

    private void endDrag(MouseEvent me) {
        // For MCVE's sake I take that the drop is over the tray.

        Bounds op = piece.localToScreen(piece.getBoundsInLocal());

        moverLayer.getChildren().remove(piece);
        // One of my several tries: mapping the coordinates till the common parent.
        // I also tried to use localtoScreen -> change parent -> screenToLocal
        Bounds b = localToParentRecursive(trayContents, desk, trayContents.getBoundsInLocal());
        Bounds b2 = localToParentRecursive(board, desk, board.getBoundsInLocal());
        trayContents.getChildren().add(piece);
        piece.setTranslateX(piece.getTranslateX() + b2.getMinX() - b.getMinX() * getScale());
        piece.setTranslateY(piece.getTranslateY() + b2.getMinY() - b.getMinY() * getScale());
        me.consume();
    }


    public static Point2D localToParentRecursive(Node n, Parent parent, double x, double y) {
        // For simplicity I suppose that the n node is on the path of the parent
        Point2D p = new Point2D(x, y);
        Node cn = n;
        while (true) {
            if (cn == parent) {
                break;
            }
            p = cn.localToParent(p);
            cn = cn.getParent();
        }
        return p;
    }

    public static Bounds localToParentRecursive(Node n, Parent parent, Bounds bounds) {
        Point2D p = localToParentRecursive(n, parent, bounds.getMinX(), bounds.getMinY());
        return new BoundingBox(p.getX(), p.getY(), bounds.getWidth(), bounds.getHeight());
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        init(primaryStage);

        primaryStage.show();
    }

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

}

嗯。经过大量的调试、计算和尝试,我终于找到了解决方案

下面是我为执行父级交换而编写的实用程序函数:

/**
 * Change the parent of a node.
 *
 * <p>
 * The node should have a common ancestor with the new parent.
 * </p>
 *
 * @param item
 *            The node to move.
 * @param newParent
 *            The new parent.
 */
@SuppressWarnings("unchecked")
public static void changeParent(Node item, Parent newParent) {
    try {
        // HAve to use reflection, because the getChildren method is protected in common ancestor of all
        // parent nodes.

        // Checking old parent for public getChildren() method
        Parent oldParent = item.getParent();
        if ((oldParent.getClass().getMethod("getChildren").getModifiers() & Modifier.PUBLIC) != Modifier.PUBLIC) {
            throw new IllegalArgumentException("Old parent has no public getChildren method.");
        }
        // Checking new parent for public getChildren() method
        if ((newParent.getClass().getMethod("getChildren").getModifiers() & Modifier.PUBLIC) != Modifier.PUBLIC) {
            throw new IllegalArgumentException("New parent has no public getChildren method.");
        }

        // Finding common ancestor for the two parents
        Parent commonAncestor = findCommonAncestor(oldParent, newParent);
        if (commonAncestor == null) {
            throw new IllegalArgumentException("Item has no common ancestor with the new parent.");
        }

        // Bounds of the item
        Bounds itemBoundsInParent = item.getBoundsInParent();

        // Mapping coordinates to common ancestor
        Bounds boundsInParentBeforeMove = localToParentRecursive(oldParent, commonAncestor, itemBoundsInParent);

        // Swapping parent
        ((Collection<Node>) oldParent.getClass().getMethod("getChildren").invoke(oldParent)).remove(item);
        ((Collection<Node>) newParent.getClass().getMethod("getChildren").invoke(newParent)).add(item);

        // Mapping coordinates back from common ancestor
        Bounds boundsInParentAfterMove = parentToLocalRecursive(newParent, commonAncestor, boundsInParentBeforeMove);

        // Setting new translation
        item.setTranslateX(
                        item.getTranslateX() + (boundsInParentAfterMove.getMinX() - itemBoundsInParent.getMinX()));
        item.setTranslateY(
                        item.getTranslateY() + (boundsInParentAfterMove.getMinY() - itemBoundsInParent.getMinY()));
    } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        throw new IllegalStateException("Error while switching parent.", e);
    }
}

/**
 * Finds the topmost common ancestor of two nodes.
 *
 * @param firstNode
 *            The first node to check.
 * @param secondNode
 *            The second node to check.
 * @return The common ancestor or null if the two node is on different
 *         parental tree.
 */
public static Parent findCommonAncestor(Node firstNode, Node secondNode) {
    // Builds up the set of all ancestor of the first node.
    Set<Node> parentalChain = new HashSet<>();
    Node cn = firstNode;
    while (cn != null) {
        parentalChain.add(cn);
        cn = cn.getParent();
    }

    // Iterates down through the second ancestor for common node.
    cn = secondNode;
    while (cn != null) {
        if (parentalChain.contains(cn)) {
            return (Parent) cn;
        }
        cn = cn.getParent();
    }
    return null;
}

/**
 * Transitively converts the coordinates from the node to an ancestor's
 * coordinate system.
 *
 * @param node
 *            The node the starting coordinates are local to.
 * @param ancestor
 *            The ancestor to map the coordinates to.
 * @param x
 *            The X of the point to be converted.
 * @param y
 *            The Y of the point to be converted.
 * @return The converted coordinates.
 */
public static Point2D localToParentRecursive(Node node, Parent ancestor, double x, double y) {
    Point2D p = new Point2D(x, y);
    Node cn = node;
    while (cn != null) {
        if (cn == ancestor) {
            return p;
        }
        p = cn.localToParent(p);
        cn = cn.getParent();
    }
    throw new IllegalStateException("The node is not a descedent of the parent.");
}

/**
 * Transitively converts the coordinates of a bound from the node to an
 * ancestor's coordinate system.
 *
 * @param node
 *            The node the starting coordinates are local to.
 * @param ancestor
 *            The ancestor to map the coordinates to.
 * @param bounds
 *            The bounds to be converted.
 * @return The converted bounds.
 */
public static Bounds localToParentRecursive(Node node, Parent ancestor, Bounds bounds) {
    Point2D p = localToParentRecursive(node, ancestor, bounds.getMinX(), bounds.getMinY());
    return new BoundingBox(p.getX(), p.getY(), bounds.getWidth(), bounds.getHeight());
}

/**
 * Transitively converts the coordinates from an ancestor's coordinate
 * system to the nodes local.
 *
 * @param node
 *            The node the resulting coordinates should be local to.
 * @param ancestor
 *            The ancestor the starting coordinates are local to.
 * @param x
 *            The X of the point to be converted.
 * @param y
 *            The Y of the point to be converted.
 * @return The converted coordinates.
 */
public static Point2D parentToLocalRecursive(Node n, Parent parent, double x, double y) {
    List<Node> parentalChain = new ArrayList<>();
    Node cn = n;
    while (cn != null) {
        if (cn == parent) {
            break;
        }
        parentalChain.add(cn);
        cn = cn.getParent();
    }
    if (cn == null) {
        throw new IllegalStateException("The node is not a descedent of the parent.");
    }

    Point2D p = new Point2D(x, y);
    for (int i = parentalChain.size() - 1; i >= 0; i--) {
        p = parentalChain.get(i).parentToLocal(p);
    }

    return p;
}

/**
 * Transitively converts the coordinates of the bounds from an ancestor's
 * coordinate system to the nodes local.
 *
 * @param node
 *            The node the resulting coordinates should be local to.
 * @param ancestor
 *            The ancestor the starting coordinates are local to.
 * @param bounds
 *            The bounds to be converted.
 * @return The converted coordinates.
 */
public static Bounds parentToLocalRecursive(Node n, Parent parent, Bounds bounds) {
    Point2D p = parentToLocalRecursive(n, parent, bounds.getMinX(), bounds.getMinY());
    return new BoundingBox(p.getX(), p.getY(), bounds.getWidth(), bounds.getHeight());
}
/**
*更改节点的父节点。
*
*
*节点应该与新父节点有一个共同的祖先。
*

* *@param项目 *要移动的节点。 *@param newParent *新的父母。 */ @抑制警告(“未选中”) 公共静态无效changeParent(节点项,父项newParent){ 试一试{ //必须使用反射,因为getChildren方法在所有方法的共同祖先中受到保护 //父节点。 //检查公共getChildren()方法的旧父级 Parent oldParent=item.getParent(); if((oldParent.getClass().getMethod(“getChildren”).getModifiers()&Modifier.PUBLIC)!=Modifier.PUBLIC){ 抛出新的IllegalArgumentException(“旧父级没有公共getChildren方法”); } //正在检查公共getChildren()方法的新父级 if((newParent.getClass().getMethod(“getChildren”).getModifiers()&Modifier.PUBLIC)!=Modifier.PUBLIC){ 抛出新的IllegalArgumentException(“新父对象没有公共getChildren方法”); } //为双亲寻找共同的祖先 父级CommonSenator=FindCommonSenator(旧父级、新父级); if(common祖先==null){ 抛出新的IllegalArgumentException(“项与新父项没有共同的祖先”); } //项目的边界 Bounds itemBoundsInParent=item.getBoundsInParent(); //将坐标映射到共同祖先 Bounds boundsInParentBeforeMove=localToParentRecursive(oldParent、Common祖先、itemBoundsInParent); //交换父级 ((集合)oldParent.getClass().getMethod(“getChildren”).invoke(oldParent)).remove(项); ((集合)newParent.getClass().getMethod(“getChildren”).invoke(newParent)).add(项); //从共同祖先映射回坐标 Bounds boundsInParentAfterMove=parentToLocalRecursive(newParent、Common祖先、boundsInParentBeforeMove); //设置新的翻译 item.setTranslateX( item.getTranslateX()+(boundsInParentAfterMove.getMinX()-itemBoundsInParent.getMinX()); item.setTranslateY( item.getTranslateY()+(boundsInParentAfterMove.getMinY()-itemBoundsInParent.getMinY()); }捕获(NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e){ 抛出新的IllegalStateException(“切换父级时出错。”,e); } } /** *查找两个节点的最上面的共同祖先。 * *@param firstNode *要检查的第一个节点。 *@param secondNode *要检查的第二个节点。 *@返回共同祖先,如果两个节点位于不同位置,则返回null *亲本树。 */ 公共静态父级FindCommon祖先(节点firstNode、节点secondNode){ //建立第一个节点的所有祖先的集合。 Set parentalChain=new HashSet(); 节点cn=第一个节点; while(cn!=null){ parentalChain.add(中国); cn=cn.getParent(); } //向下遍历公共节点的第二个祖先。 cn=第二节点; while(cn!=null){ if(parentalChain.contains(cn)){ 返回(母公司)cn; } cn=cn.getParent(); } 返回null; } /** *可传递地将坐标从节点转换为祖先的坐标 *坐标系。 * *@param节点 *起始坐标所在的节点。 *@param祖先 *要将坐标映射到的祖先。 *@param x *要转换的点的X。 *@param y *要转换的点的Y轴。 *@返回转换后的坐标。 */ 公共静态点2D LocalTopArentrCursive(节点节点、父祖先、双x、双y){ 点2dp=新点2d(x,y); 节点cn=节点; while(cn!=null){ if(cn==祖先){ 返回p; } p=cn.localToParent(p); cn=cn.getParent(); } 抛出新的非法状态异常(“否