使用 JavaFX 将图像拖到 window 之外

Drag Image outside the window with JavaFX

大家,

我想在我的应用程序中插入一个可以从应用程序中拖放的图形。一旦图形在 window 之外发布,应打开未解码/透明 window,其中仅显示此图形。

    Stage newStage = new Stage();
    StackPane stack = new StackPane();
    ImageView imageView = new ImageView(new Image(this.getClass().getResourceAsStream(InBoxEnum.Graphic.INBOXLOGO.getFilename())));
    stack.getChildren().add(imageView);


    imageView.setOnDragDetected(event -> {
        Dragboard dragboard = imageView.startDragAndDrop(TransferMode.MOVE);

        dragboard.setDragView(imageView.snapshot(null, null));
        ClipboardContent content = new ClipboardContent();
        content.put(DRAGGABLE_INBOX_TYPE, "dontcare");
        dragboard.setContent(content);

        event.consume();
    });

    imageView.setOnDragDone(event -> {
        System.out.println(event.getScreenX());
        System.out.println(event.getScreenY());
    });

    Scene scene = new Scene(stack, 500, 500);
    newStage.setScene(scene);
    newStage.show();

DragDetected 事件到目前为止也没有问题。

问题是在 dragDone 事件中,鼠标的位置始终为 0,我无法判断鼠标是在我的应用程序内部还是外部。如果在应用程序内松开鼠标,则不会发生任何事情。

我也试过机器人 Class,但我总是得到一个静态的奇怪 x/y 位置。

我正在使用JAVA11(采纳JDK)。

感谢您的帮助

使用 Robot 对我来说效果很好(Oracle JDK 11 + JavaFX 12)。由于您实际上不想拖放任何图像数据,因此您可以通过立即创建一个新舞台并使用 ImageViewMOUSE_DRAGGED 更新 [=17] 的位置来简单地解决此问题=]:

Stage newStage = new Stage();
StackPane stack = new StackPane();
ImageView imageView = new ImageView(new Image(...));
stack.getChildren().add(imageView);

class DragHandler implements EventHandler<MouseEvent> {

    Stage dragTarget;

    @Override
    public void handle(MouseEvent event) {
        if (dragTarget != null) {
            // move stage
            dragTarget.setX(event.getScreenX());
            dragTarget.setY(event.getScreenY());
            event.consume();
        }
    }

}

final DragHandler dragHandler = new DragHandler();

imageView.setOnDragDetected(event -> {
    // init stage at half transparency
    Group root = new Group(new ImageView(imageView.getImage())); 
    root.setOpacity(0.5);

    Scene displayScene = new Scene(root);
    displayScene.setFill(null);
    Stage displayStage = new Stage();
    displayStage.initStyle(StageStyle.TRANSPARENT);
    displayStage.setScene(displayScene);
    displayStage.setX(event.getScreenX());
    displayStage.setY(event.getScreenY());
    displayStage.show();

    dragHandler.dragTarget = displayStage;

    event.consume();
});
imageView.setOnMouseDragged(dragHandler);
imageView.setOnMouseReleased(event -> {
    if (dragHandler.dragTarget != null) {
        if (stack.contains(event.getX(), event.getY())) { // check, if drop happened inside the bounds of the scene root
            dragHandler.dragTarget.hide();
        } else {
            // make stage fully opaque & cleanup
            dragHandler.dragTarget.getScene().getRoot().setOpacity(1);
            imageView.setImage(null);
        }
        dragHandler.dragTarget = null;
        event.consume();
    }
});

Scene scene = new Scene(stack, 500, 500);
newStage.setScene(scene);
newStage.show();

建议的解决方案基于
主要更改封装在 withinBounds 方法中,该方法用于定义拖动是否在 window 范围内结束。此方法基于this答案:

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.Window;

public class DragOut extends Application {

    @Override
    public void start(Stage primaryStage) {

        ImageView imageView = new ImageView("https://findicons.com/files/icons/345/summer/128/cake.png");
        StackPane stack = new StackPane(imageView);

        DragHandler dragHandler = new DragHandler();
        imageView.setOnDragDetected(event -> {
            Stage displayStage = makeNewStage(imageView.getImage());
            displayStage.setX(event.getScreenX());
            displayStage.setY(event.getScreenY());
            dragHandler.setStage(displayStage);
            displayStage.show();
            event.consume();
        });
        imageView.setOnMouseDragged(dragHandler);
        imageView.setOnMouseReleased(event -> {
            if (! withinBounds(event, imageView.getScene().getWindow()) && dragHandler.dragTarget != null) {
                // make stage fully opaque & cleanup
                dragHandler.showStage();
                imageView.setImage(null);
                event.consume();
            }else{
                dragHandler.closeStage();
            }
            dragHandler.setStage(null);
        });

        Scene scene = new Scene(stack, 300, 300);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private Stage makeNewStage(Image image) {

        Group root = new Group(new ImageView(image));
        root.setOpacity(0.5); // init stage at half transparency
        Stage displayStage = new Stage(StageStyle.TRANSPARENT);
        displayStage.setScene(new Scene(root, null));

        return displayStage;
    }

    private boolean withinBounds(MouseEvent event, Window window) {

        Point2D mouseLoc = new Point2D(event.getScreenX(), event.getScreenY());
        Rectangle2D windowBounds = new Rectangle2D(window.getX(), window.getY(),
                                            window.getWidth(), window.getHeight());
        return windowBounds.contains(mouseLoc);
    }

    class DragHandler implements EventHandler<MouseEvent> {

        private Stage dragTarget;

        @Override
        public void handle(MouseEvent event) {
            if (dragTarget != null) {
                // move stage
                dragTarget.setX(event.getScreenX());
                dragTarget.setY(event.getScreenY());
                event.consume();
            }
        }

        void setStage(Stage stage){
            dragTarget = stage;
        }

        void showStage(){
            if (dragTarget != null) {
                dragTarget.getScene().getRoot().setOpacity(1);
            }
        }

        void closeStage(){
            if (dragTarget != null) {
                dragTarget.close();
            }
        }
    }

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