【问题标题】:Drag nodes like in a patience/Klondike card game像耐心/克朗代克纸牌游戏一样拖动节点
【发布时间】:2015-04-02 17:12:30
【问题描述】:

我正在做一个克朗代克游戏。逻辑一切正常。我只是在 javafx 中的 UI 遇到问题。

我一直在尝试从“画面堆”周围移动/拖动卡片,但没有得到预期的结果。 我的卡片是一个 ImageView,里面有一个 Image。卡片位于窗格内:

Pane tableau = new Pane();
for (int i = 0; i < n; i++) {
    Image img = new Image("resources/images/" + (i + 1) + ".png");
    ImageView imgView = new ImageView(img);
    imgView.setY(i * 20);
    //imgView Mouse Events here
    tableau.getChildren().add(imgView);
}

我试过了:

imgView.setOnMousePressed((MouseEvent mouseEvent) -> {
    dragDelta.x = imgView.getLayoutX() - mouseEvent.getSceneX();
    dragDelta.y = imgView.getLayoutY() - mouseEvent.getSceneY();
});
imgView.setOnMouseDragged((MouseEvent mouseEvent) -> {
    imgView.setLayoutX(mouseEvent.getSceneX() + dragDelta.x);
    imgView.setLayoutY(mouseEvent.getSceneY() + dragDelta.y);
});

此解决方案不起作用,因为我正在设置位置,因此当释放卡片时不会返回到原来的位置,并且因为卡片与其他 UI 对象发生碰撞。

另一个尝试:

imgView.setOnDragDetected((MouseEvent event) -> {
    ClipboardContent content = new ClipboardContent();
    content.putImage(img);
    Dragboard db = imgView.startDragAndDrop(TransferMode.ANY);
    db.setDragView(img, 35, 50);
    db.setContent(content);
    event.consume();
});

在这个解决方案中,问题是:卡片是半透明的,就像移动文件一样,光标变为 no/forbidden 但除此之外它运行良好:没有冲突,如果我松开鼠标,卡片会回到原来的位置。 另一个问题是我不知道我是否可以使用此解决方案移动超过 1 张卡?

我的最后一个问题是,我怎样才能将一个节点(在本例中为 ImageView)或一组节点从一堆移动到另一堆,就像在纸牌游戏中一样?

【问题讨论】:

    标签: java javafx draggable


    【解决方案1】:

    要知道原始卡片位置,您应该在鼠标处理程序中使用setTranslateX(和 Y)而不是 setLayoutX。所以当用户释放卡片时,你可以简单地调用一个Transition,让卡片飞回布局位置。如果用户在有效位置释放卡片,则将平移坐标设置为 0 并更改布局位置或使用relocate

    如果你想让卡片半透明,你可以 e. G。更改 opacity 或应用 CSS。

    顺便说一句,我不会使用剪贴板内容,它似乎不适合您的需求。

    您可以使用鼠标处理代码移动多个对象。您只需将翻译同时应用于多个对象。当您拖动一堆时,您确定所选卡片顶部的卡片并将过渡应用于所有卡片。


    这是一个简单的示例,向您展示它的外观。

    Card.java,您将使用 ImageView。

    public class Card extends Rectangle {
    
        static Random rand = new Random();
    
        public Card() {
    
    
            setWidth(100);
            setHeight(200);
    
            Color color = createRandomColor();
    
            setStroke(color);
            setFill( color.deriveColor(1, 1, 1, 0.4));
    
        }
    
        public static Color createRandomColor() {
    
            int max = 200;
    
            Color color = Color.rgb( (int) (rand.nextDouble() * max), (int) (rand.nextDouble() * max), (int) (rand.nextDouble() * max));
    
            return color;
        }
    }
    

    Game.java,应用程序。

    public class Game extends Application {
    
        static List<Card> cardList = new ArrayList<>();
    
        @Override
        public void start(Stage primaryStage) {
    
            MouseGestures mg = new MouseGestures();
    
            Group root = new Group();
    
            for( int i=0; i < 10; i++) {
    
                Card card = new Card();
                card.relocate( i * 20, i * 10);
    
                mg.makeDraggable(card);
    
                cardList.add( card);
            }
    
            root.getChildren().addAll( cardList);
    
            Scene scene = new Scene( root, 1600, 900);
    
            primaryStage.setScene( scene);
            primaryStage.show();
    
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
        // TODO: don't use a static method, I only added for the example 
        public static List<Card> getSelectedCards( Card currentCard) {
    
            List<Card> selectedCards = new ArrayList<>();
    
            int i = cardList.indexOf(currentCard);
            for( int j=i + 1; j < cardList.size(); j++) {
                selectedCards.add( cardList.get( j));
            }
    
            return selectedCards;
        }
    
    }
    

    MouseGestures.java,鼠标处理机制。

    public class MouseGestures {
    
        final DragContext dragContext = new DragContext();
    
        public void makeDraggable(final Node node) {
    
            node.setOnMousePressed(onMousePressedEventHandler);
            node.setOnMouseDragged(onMouseDraggedEventHandler);
            node.setOnMouseReleased(onMouseReleasedEventHandler);
    
        }
    
        EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
    
            @Override
            public void handle(MouseEvent event) {
    
                dragContext.x = event.getSceneX();
                dragContext.y = event.getSceneY();
    
            }
        };
    
        EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
    
            @Override
            public void handle(MouseEvent event) {
    
                Node node = (Node) event.getSource();
    
                double offsetX = event.getSceneX() - dragContext.x;
                double offsetY = event.getSceneY() - dragContext.y;
    
                node.setTranslateX(offsetX);
                node.setTranslateY(offsetY);
    
                // same for the other cards
                List<Card> list = Game.getSelectedCards( (Card) node);
                for( Card card: list) {
                    card.setTranslateX(offsetX);
                    card.setTranslateY(offsetY);
                }
    
    
            }
        };
    
        EventHandler<MouseEvent> onMouseReleasedEventHandler = new EventHandler<MouseEvent>() {
    
            @Override
            public void handle(MouseEvent event) {
    
                Node node = (Node) event.getSource();
    
                moveToSource(node);
    
                // same for the other cards
                List<Card> list = Game.getSelectedCards( (Card) node);
                for( Card card: list) {
                    moveToSource(card);
                }
    
                // if you find out that the cards are on a valid position, you need to fix it, ie invoke relocate and set the translation to 0
                // fixPosition( node);
    
            }
        };
    
        private void moveToSource( Node node) {
            double sourceX = node.getLayoutX() + node.getTranslateX();
            double sourceY = node.getLayoutY() + node.getTranslateY();
    
            double targetX = node.getLayoutX();
            double targetY = node.getLayoutY();
    
            Path path = new Path();
            path.getElements().add(new MoveToAbs( node, sourceX, sourceY));
            path.getElements().add(new LineToAbs( node, targetX, targetY));
    
            PathTransition pathTransition = new PathTransition();
            pathTransition.setDuration(Duration.millis(1000));
            pathTransition.setNode(node);
            pathTransition.setPath(path);
            pathTransition.setCycleCount(1);
            pathTransition.setAutoReverse(true);
    
            pathTransition.play();
        }
    
        /**
         * Relocate card to current position and set translate to 0.
         * @param node
         */
        private void fixPosition( Node node) {
    
            double x = node.getTranslateX();
            double y = node.getTranslateY();
    
            node.relocate(node.getLayoutX() + x, node.getLayoutY() + y);
    
            node.setTranslateX(0);
            node.setTranslateY(0);
    
        }
    
        class DragContext {
    
            double x;
            double y;
    
        }
    
        // pathtransition works with the center of the node => we need to consider that
        public static class MoveToAbs extends MoveTo {
    
            public MoveToAbs( Node node, double x, double y) {
                super( x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
    
            }
    
        }
    
        // pathtransition works with the center of the node => we need to consider that
        public static class LineToAbs extends LineTo {
    
            public LineToAbs( Node node, double x, double y) {
                super( x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
            }
    
        }
    
    }
    

    当您选择一张或多张卡片时,它们会转换回原来的位置。

    这是我从顶部拖动第三张卡片的屏幕截图。

    当您检查卡片是否位于有效目的地时,您应该调用 fixPosition 方法。它只是重新定位卡,即。 e.使用平移值计算位置,重新定位节点并将其平移设置为 0。

    棘手的部分是 PathTransition。它从节点的中心工作,而不是从 x/y 坐标。 Here's 一个关于该问题的线程,如果您想了解更多信息,我会回复它。

    【讨论】:

    • 哇,很好的例子+解释,竖起大拇指。你是为了这个答案而写这段代码还是手背上有什么东西?
    • 谢谢,从头开始只是为了这个答案。无论如何,我想在某个时候做一个纸牌游戏,所以我想为什么不在别人需要它的时候创建基础:-)
    • 感谢您的回答,对我有很大帮助。
    • 如果我想要多堆,每堆都需要一个cardList。所以我需要改变 MouseGuestures 只移动那堆牌。有一种简单的方法可以做到这一点吗?每张卡片都应该知道它们的父级 (cardList),因此 MouseGuestures 只会移动该卡片列表上的卡片。
    • 是的,这是一种方法,即给每张卡一个父属性,也可能是一个子属性。另一种解决方案是为每个堆使用一个堆栈或一个 ArrayList,就像我在示例中所做的那样。毕竟,当您将一堆卡片移动到另一堆卡片时,您所做的就是将卡片的子列表移动到另一个卡片列表。
    猜你喜欢
    • 1970-01-01
    • 2014-09-20
    • 2010-10-12
    • 1970-01-01
    • 2012-05-07
    • 2011-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多