在JavaFX中同时处理KeyEvent和MouseEvent

时间:2017-03-16 10:53:30

标签: java javafx event-handling

我正在创建一个游戏,用户需要将ImageView拖到GridPane上,当他们拖动它时,我希望他们能够按一个键来旋转ImageView。我只希望它们能够在拖动时旋转ImageView,因此我在拖动事件开始时设置KeyEvent处理程序,然后将其删除。

以下是设置处理程序的代码:

private void setPlacementHandlers()
{
    setOnMouseMoved(event -> trackMouse(event));
    setOnDragDetected(event -> pickedUp(event));
    setOnDragDone(event -> placed(event));
}      

以下是OnDragDetected的代码:

private void pickedUp(MouseEvent event)
{
    //Get variables from Ship
    ShipPart[] parts = ship.getParts();
    int shipSize = ship.getSize();

    Dragboard db = this.startDragAndDrop(TransferMode.MOVE);

    ship.getBoard().setDraggedPart(this);

    ClipboardContent cbContent = new ClipboardContent();
    cbContent.putImage(this.getImage());

    db.setDragView(shipImage);

    db.setDragViewOffsetX((partIndex * 50) + mouseX);
    db.setDragViewOffsetY(mouseY);

    db.setContent(cbContent);

    setFocusTraversable(true);
    requestFocus();

    setOnKeyPressed(keyEvent -> rotate(keyEvent, db, cbContent));
    getScene().onKeyPressedProperty().bind(this.onKeyPressedProperty());

    for(int i = 0; i < shipSize; i++)
        parts[i].setVisible(false);

    event.consume();
}

以下是OnKeyPressed的临时版本,我将其用于测试目的:

private void rotate(KeyEvent keyEvent, Dragboard db, ClipboardContent cbContent)
{
    System.out.println("Potato");
}

我试过setFocusTraversable(true);和requestFocus();

出于调试的目的,我删除了在拖动完成后删除KeyEvent处理程序的代码,结果发现KeyEvent在DragEvent之后工作正常,但在此期间没有。

总而言之,问题似乎并不是ImageView没有获得KeyEvent,看起来好像KeyEvent甚至没有因DragEvent而生成。

感谢任何帮助。

更新:我还尝试将KeyEvent处理程序添加到另一个对象,然后将更改应用于我想要更改的对象,但这并不起作用。我还尝试了各种线程和任务组合。

1 个答案:

答案 0 :(得分:1)

拖放操作是本机系统操作。它显示的任何图形都是本机对象,而不是Java应用程序的一部分,尽管系统为应用程序提供了有限的配置能力,Java通过Dragboard等类提供访问。

由于拖动的对象不是JavaFX节点,因此无法在其上侦听(大多数)关键事件。实际上,在拖动过程中,程序中没有Node可能没有键盘焦点。

如果拖动源和拖动目标都在同一个场景中,您可以自己模拟拖动,方法是将场景的内容放在StackPane中,然后在同一个StackPane中放置一个“拖动窗格”,这样就可以了内容的顶部,带有一个剪辑,确保它不会阻止内容的键盘或鼠标事件。拖动窗格同时包含可拖动节点的副本(在您的情况下为ImageView),以及用于突出显示当前拖动下的任何节点的形状。

import java.nio.file.Paths;
import java.util.List;

import javafx.application.Application;

import javafx.beans.Observable;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;

import javafx.stage.Stage;

import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.SplitPane;
import javafx.scene.image.ImageView;

import javafx.scene.shape.Polyline;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;

import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;

import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;

import javafx.scene.paint.Color;

public class ImageDragExample
extends Application {
    private static final String DEFAULT_IMAGE =
        "http://solarsystem.nasa.gov/images/galleries/618486main_earth_320.jpg";

    private GridPane grid;

    private ImageView preview;
    private ImageView dragImage;
    private Pane dragPane;
    private Rectangle dragPaneClip;

    private Point2D dragStart;
    private Node dragTarget;
    private Shape dragHighlight;

    @Override
    public void start(Stage stage) {
        grid = new GridPane();
        grid.setHgap(24);
        grid.setVgap(24);
        grid.setPadding(new Insets(12));
        int id = 0;
        for (int row = 0; row < 3; row++) {
            Node p1 = createPlaceholder(String.valueOf(row * 4 + 1));
            Node p2 = createPlaceholder(String.valueOf(row * 4 + 2));
            Node p3 = createPlaceholder(String.valueOf(row * 4 + 3));
            Node p4 = createPlaceholder(String.valueOf(row * 4 + 4));
            grid.addRow(row, p1, p2, p3, p4);
        }

        List<String> args = getParameters().getRaw();
        preview = new ImageView(args.isEmpty() ? DEFAULT_IMAGE :
            Paths.get(args.get(0)).toUri().toString());

        BorderPane imageArea = new BorderPane(preview);
        imageArea.setPadding(new Insets(12));

        Region contents = new BorderPane(new SplitPane(grid, imageArea));

        dragImage = new ImageView();
        dragImage.imageProperty().bind(preview.imageProperty());
        dragImage.setFocusTraversable(true);
        dragImage.setOpacity(0);
        dragImage.setOnMousePressed(e -> startDrag(e));
        dragImage.setOnMouseDragged(e -> updateDrag(e));
        dragImage.setOnMouseReleased(e -> endDrag(e));
        dragImage.setOnKeyPressed(e -> rotate(e));

        dragPane = new AnchorPane(dragImage);
        dragPaneClip = new Rectangle();
        dragPane.setClip(dragPaneClip);
        StackPane pane = new StackPane(contents, dragPane);

        Scene scene = new Scene(pane);
        stage.setScene(scene);

        preview.boundsInParentProperty().addListener(this::placeDragImage);
        imageArea.boundsInParentProperty().addListener(this::placeDragImage);
        stage.widthProperty().addListener(this::placeDragImage);
        stage.heightProperty().addListener(this::placeDragImage);
        placeDragImage(null);

        stage.setTitle("Image Drag Example");
        stage.show();
    }

    private void rotate(KeyEvent e){
        System.out.println(e.getCode() + " pressed");
    }

    private void startDrag(MouseEvent event){
        dragStart = new Point2D(event.getScreenX(), event.getScreenY());
        dragImage.setOpacity(0.5);
        dragImage.requestFocus();
        dragPane.setClip(null);
    }

    private void updateDrag(MouseEvent event){
        double x = event.getScreenX();
        double y = event.getScreenY();
        double xDelta = x - dragStart.getX();
        double yDelta = y - dragStart.getY();
        dragImage.setTranslateX(xDelta);
        dragImage.setTranslateY(yDelta);

        Node newDragTarget = null;
        for (Node node : grid.getChildren()) {
            if (node.contains(node.screenToLocal(x, y))) {
                newDragTarget = node;
                break;
            }
        }

        removeDragHighlight();
        dragTarget = newDragTarget;
        addDragHighlight(dragTarget);
    }

    private void endDrag(MouseEvent event){
        if (dragTarget != null) {
            removeDragHighlight();
            System.out.println("Dropped on " + dragTarget.getId());
        }

        dragImage.setOpacity(0);
        dragImage.setTranslateX(0);
        dragImage.setTranslateY(0);
        dragPane.setClip(dragPaneClip);
        dragStart = null;
        dragTarget = null;
    }

    private void placeDragImage(Observable o) {
        if (preview.getScene() != null && dragImage.getScene() != null) {
            Point2D point = preview.localToScene(0, 0);
            if (point != null) {
                point = dragImage.getParent().sceneToLocal(point);
                AnchorPane.setLeftAnchor(dragImage, point.getX());
                AnchorPane.setTopAnchor(dragImage, point.getY());
                dragPaneClip.setX(point.getX());
                dragPaneClip.setY(point.getY());
                dragPaneClip.setWidth(dragImage.getLayoutBounds().getWidth());
                dragPaneClip.setHeight(dragImage.getLayoutBounds().getHeight());
            }
        }
    }

    private void addDragHighlight(Node node) {
        if (node != null) {
            Bounds bounds = node.getBoundsInLocal();

            dragHighlight = null;
            if (node instanceof Region) {
                dragHighlight = ((Region) node).getShape();
            }
            if (dragHighlight == null) {
                dragHighlight = new Rectangle(
                    bounds.getMinX(), bounds.getMinY(),
                    bounds.getWidth(), bounds.getHeight());
            } else {
                // Clone the node's shape by subtracting an empty shape from it.
                dragHighlight = Shape.subtract(dragHighlight, new Polyline());
            }
            dragHighlight.setStrokeWidth(2);
            dragHighlight.setStroke(Color.RED);
            dragHighlight.setFill(null);

            bounds = dragHighlight.getBoundsInLocal();
            Point2D shapeOrigin = 
                node.localToScene(bounds.getMinX(), bounds.getMinY());
            shapeOrigin = dragPane.sceneToLocal(shapeOrigin);

            dragPane.getChildren().add(dragHighlight);
            AnchorPane.setLeftAnchor(dragHighlight, shapeOrigin.getX());
            AnchorPane.setTopAnchor(dragHighlight, shapeOrigin.getY());
        }
    }

    private void removeDragHighlight() {
        if (dragHighlight != null) {
            dragPane.getChildren().remove(dragHighlight);
            dragHighlight = null;
        }
    }

    private Node createPlaceholder(String id) {
        Label label = new Label(id);
        label.setId(id);
        label.setAlignment(Pos.CENTER);
        label.setStyle("-fx-background-color: #c0c0ff;");
        label.setPrefSize(200, 200);
        return label;
    }
}