Proper way to move a JavaFx8 node around

2019-09-05 04:40发布

I am working on an app which needs to move nodes around on a pane.

I've just recently started learning javaFx 8 so I have bare understanding of the workflow, but while I managed to get a working draggable node code working, it uses node.setTranslateX() and .setTranslateY().

This is all fine, until I tried to make the node snap to a grid that divides the scene area in both 10 height and width subdivisions.

Whenever I try to use modulos to get some sort of snapping going on, I'm stuck with the fact that I'm calling Translation transformation. I'd like to directly set the coordinates of the node myself. I've tried node.relocate(x,y) to no avail. As for node.setX() or node.setY(). These do no seem to do much.

So basically, I have two questions:

  • Is there a way to directly set the coordinates of a node ? If yes, how ?
  • What is the correct way to snap a node to a grid while dragging it with the mouse ?

My current code uses these methods to drag the node around:

public class Boat extends ImageView {
    private int size ;
    private String name ;
    private Double dragDeltaX, dragDeltaY;

    //Constructor etc here//

    this.setOnMousePressed(event -> {
        this.setCursor(Cursor.CLOSED_HAND);
        dragDeltaX = this.getLayoutX() + event.getX();
        dragDeltaY = this.getLayoutY() + event.getY();
    });

    this.setOnMouseReleased(event -> {
        this.setCursor(Cursor.OPEN_HAND);
        this.relocate(event.getSceneX(), event.getSceneY());
    });

    this.setOnMouseDragged(event -> {
        this.setTranslateX(event.getSceneX() - dragDeltaX);
        this.setTranslateY(event.getSceneY() - dragDeltaY);
    });

    this.setOnMouseEntered(event -> {
        this.setCursor(Cursor.OPEN_HAND);
    });
}

Thanks in advance for the help,

1条回答
Explosion°爆炸
2楼-- · 2019-09-05 05:17

In most Pane subclasses, if a Node is managed, then the parent pane of the node will manage its layoutX and layoutY properties. So setting layoutX and layoutY for these nodes will have no visible effect on the position of the node.

Transformations (including the translation defined by translateX and translateY) are applied to nodes after the layout calculations (whether the node is managed by its parent or not).

So one way to manage dragging is just to manipulate the translateX and translateY properties. Another way is to manipulate the layoutX and layoutY properties (by setting them directly or by calling relocate), and make sure the node is not managed by its parent. I would not recommend mixing the two techniques, as your code will be harder to follow.

You can make the node unmanaged setManaged(false) on the node, or by putting it in a Pane instead of one of the subclasses (Pane does not manage layout of its child nodes).

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class ImageViewDragExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        Image image = createImage();
        DraggableImageView imageView = new DraggableImageView(image);

        // if the node is placed in a parent that manages its child nodes,
        // you must call setManaged(false);

//      imageView.setManaged(false);
//      StackPane root = new StackPane(imageView);

        // or use a plain `Pane`, which does not manage its child nodes:
        Pane root = new Pane(imageView);

        Scene scene = new Scene(root, 400, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private Image createImage() {
        WritableImage image = new WritableImage(50, 50);
        for (int y = 0 ; y < 50; y++) {
            for (int x = 0 ; x < 50 ; x++) {
                Color c ;
                if ((x > 20 && x < 30) || (y > 20 && y < 30)) {
                    c = Color.AZURE ;
                } else {
                    c = Color.CORNFLOWERBLUE ;
                }
                image.getPixelWriter().setColor(x, y, c);
            }
        }
        return image ;
    }

    public static class DraggableImageView extends ImageView {
        private double mouseX ;
        private double mouseY ;
        public DraggableImageView(Image image) {
            super(image);

            setOnMousePressed(event -> {
                mouseX = event.getSceneX() ;
                mouseY = event.getSceneY() ;
            });

            setOnMouseDragged(event -> {
               double deltaX = event.getSceneX() - mouseX ;
               double deltaY = event.getSceneY() - mouseY ;
               relocate(getLayoutX() + deltaX, getLayoutY() + deltaY);
               mouseX = event.getSceneX() ;
               mouseY = event.getSceneY() ;
            });
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
查看更多
登录 后发表回答