实现拖动多个选定节点 [英] Implement dragging multiple selected nodes

查看:61
本文介绍了实现拖动多个选定节点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  • 我有一个扩展 javafx.scene.Node 的类,例如DraggableNode.
  • 我已经编写了用于拖曳任何此类 DraggableNode 的事件处理程序.

  • I have a class which extends javafx.scene.Node, say DraggableNode.
  • I have written the event-handlers for dragging for any such DraggableNode .

Class DraggableNode extends Node
{
    ...
     onMouseDraggedProperty().set(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
               offsetX = dragStartPoitionX - event.getSceneX();
               offsetY = dragStartPoitionY - event.getSceneY();
               setLayoutX (event.getSceneX());
               setLayoutY (event.getSceneY());
                 ...
            }
     }
 }

  • 此事件处理程序可以单独拖动该节点.
  • 接下来,我需要选择多个此类节点",并且拖动所选节点之一应通过"offsetX" 更改所有所选节点的(x,y)坐标. & "offsetY" .
  • 还实现了
  • 选择算法(在扩展 Pane 的类中,其中将这些节点添加为子级).但是,我需要以某种方式 trigger 其他选定节点的拖动事件处理程序,以便最终输出看起来像是多次拖动.
  • This event-handler works fine for dragging this node individually.
  • Next, I require to select multiple such "nodes" and, dragging of one of the selected node should change the (x,y) co-ordinates of all the selected nodes by "offsetX" & "offsetY".
  • Selection algorithm is also implemented(in a Class extending Pane in which these nodes are added as children). But, what I need is to somehow trigger the drag event handlers of the other selected nodes so that the final output would look like a multi-drag.
  • 推荐答案

    首先创建一个选择模型,例如Set< Node>.只要将节点添加到选择中,就将其添加到选择模型中.

    First you create a selection model, say a Set<Node>. Whenever you add a Node to your selection, you add it to the selection model.

    拖动节点时,您只需更改事件处理程序中选择模型的所有其他节点的位置即可.

    When you drag a node, you simply change the position of all of the other nodes of the selection model in your event handler as well.

    就这么简单.

    此处的代码还支持橡皮筋选择,平移&在选择等操作时按Ctrl键:

    Here's code which also supports rubberband selection, shift & ctrl keypress during selection, etc:

    NodeSelection.java

    import java.util.Arrays;
    import java.util.HashSet;
    import java.util.Random;
    import java.util.Set;
    
    import javafx.application.Application;
    import javafx.event.EventHandler;
    import javafx.scene.Node;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.image.Image;
    import javafx.scene.image.ImageView;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.layout.Pane;
    import javafx.scene.layout.Region;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Rectangle;
    import javafx.scene.shape.StrokeLineCap;
    import javafx.stage.Stage;
    
    public class NodeSelection extends Application {
    
        public static Image image = new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/320px-Siberischer_tiger_de_edit02.jpg");
    //  public Image image = new Image( getClass().getResource( "tiger.jpg").toExternalForm());
    
        SelectionModel selectionModel = new SelectionModel();
    
        DragMouseGestures dragMouseGestures = new DragMouseGestures();
    
        static Random rnd = new Random();
    
        @Override
        public void start(Stage primaryStage) {
    
            Pane pane = new Pane();
            pane.setStyle("-fx-background-color:white");
    
            new RubberBandSelection( pane);
    
            double width = 200;
            double height = 160;
    
            double padding = 20;
            for( int row=0; row < 4; row++) {
                for( int col=0; col < 4; col++) {
    
                    Selectable selectable = new Selectable( width, height);
                    selectable.relocate( padding * (col+1) + width * col, padding * (row + 1) + height * row);
    
                    pane.getChildren().add(selectable);
    
                    dragMouseGestures.makeDraggable(selectable);
    
                }
            }
    
            Label infoLabel = new Label( "Drag on scene for Rubberband Selection. Shift+Click to add to selection, CTRL+Click to toggle selection. Drag selected nodes for multi-dragging.");
            pane.getChildren().add( infoLabel);
    
            Scene scene = new Scene( pane, 1600, 900);
            scene.getStylesheets().add( getClass().getResource("application.css").toExternalForm());
    
            primaryStage.setScene( scene);
            primaryStage.show();        
    
    
    
        }
    
        private class Selectable extends Region {
    
            ImageView view;
    
            public Selectable( double width, double height) {
    
                view = new ImageView( image);
                view.setFitWidth(width);
                view.setFitHeight(height);
    
                getChildren().add( view);
    
                this.setPrefSize(width, height);
            }
    
        }
    
        private class SelectionModel {
    
            Set<Node> selection = new HashSet<>();
    
            public void add( Node node) {
    
                if( !node.getStyleClass().contains("highlight")) {
                    node.getStyleClass().add( "highlight");
                }
    
                selection.add( node);
            }
    
            public void remove( Node node) {
                node.getStyleClass().remove( "highlight");
                selection.remove( node);
            }
    
            public void clear() {
    
                while( !selection.isEmpty()) {
                    remove( selection.iterator().next());
                }
    
            }
    
            public boolean contains( Node node) {
                return selection.contains(node);
            }
    
            public int size() {
                return selection.size();
            }
    
            public void log() {
                System.out.println( "Items in model: " + Arrays.asList( selection.toArray()));
            }
    
        }
    
        private class DragMouseGestures {
    
            final DragContext dragContext = new DragContext();
    
            private boolean enabled = false;
    
            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) {
    
                    // don't do anything if the user is in the process of adding to the selection model
                    if( event.isControlDown() || event.isShiftDown())
                        return;
    
                    Node node = (Node) event.getSource();
    
                    dragContext.x = node.getTranslateX() - event.getSceneX();
                    dragContext.y = node.getTranslateY() - event.getSceneY();
    
                    // clear the model if the current node isn't in the selection => new selection
                    if( !selectionModel.contains(node)) {
                        selectionModel.clear();
                        selectionModel.add( node);
                    }
    
                    // flag that the mouse released handler should consume the event, so it won't bubble up to the pane which has a rubberband selection mouse released handler
                    enabled = true;
    
                    // prevent rubberband selection handler 
                    event.consume();
                }
            };
    
            EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
    
                @Override
                public void handle(MouseEvent event) {
    
                    if( !enabled)
                        return;
    
                    // all in selection
                    for( Node node: selectionModel.selection) {
                        node.setTranslateX( dragContext.x + event.getSceneX());
                        node.setTranslateY( dragContext.y + event.getSceneY());
                    }
    
                }
            };
    
            EventHandler<MouseEvent> onMouseReleasedEventHandler = new EventHandler<MouseEvent>() {
    
                @Override
                public void handle(MouseEvent event) {
    
                    // prevent rubberband selection handler 
                    if( enabled) {
    
                        // set node's layout position to current position,remove translate coordinates
                        for( Node node: selectionModel.selection) {
                            fixPosition(node);
                        }
    
                        enabled = false;
    
                        event.consume();
                    }
                }
            };
    
            /**
             * Set node's layout position to current position, remove translate coordinates.
             * @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;
    
            }
    
        }
    
        private class RubberBandSelection {
    
            final DragContext dragContext = new DragContext();
            Rectangle rect;
    
            Pane group;
            boolean enabled = false;
    
            public RubberBandSelection( Pane group) {
    
                this.group = group;
    
                rect = new Rectangle( 0,0,0,0);
                rect.setStroke(Color.BLUE);
                rect.setStrokeWidth(1);
                rect.setStrokeLineCap(StrokeLineCap.ROUND);
                rect.setFill(Color.LIGHTBLUE.deriveColor(0, 1.2, 1, 0.6));
    
                group.addEventHandler(MouseEvent.MOUSE_PRESSED, onMousePressedEventHandler);
                group.addEventHandler(MouseEvent.MOUSE_DRAGGED, onMouseDraggedEventHandler);
                group.addEventHandler(MouseEvent.MOUSE_RELEASED, onMouseReleasedEventHandler);
    
            }
    
            EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
    
                @Override
                public void handle(MouseEvent event) {
    
                    // simple flag to prevent multiple handling of this event or we'd get an exception because rect is already on the scene
                    // eg if you drag with left mouse button and while doing that click the right mouse button
                    if( enabled)
                        return;
    
                    dragContext.mouseAnchorX = event.getSceneX();
                    dragContext.mouseAnchorY = event.getSceneY();
    
                    rect.setX(dragContext.mouseAnchorX);
                    rect.setY(dragContext.mouseAnchorY);
                    rect.setWidth(0);
                    rect.setHeight(0);
    
                    group.getChildren().add( rect);
    
                    event.consume();
    
                    enabled = true;
                }
            };
    
            EventHandler<MouseEvent> onMouseReleasedEventHandler = new EventHandler<MouseEvent>() {
    
                @Override
                public void handle(MouseEvent event) {
    
                    if( !event.isShiftDown() && !event.isControlDown()) {
                        selectionModel.clear();
                    }
    
                    for( Node node: group.getChildren()) {
    
                        if( node instanceof Selectable) {
                            if( node.getBoundsInParent().intersects( rect.getBoundsInParent())) {
    
                                if( event.isShiftDown()) {
    
                                    selectionModel.add( node);
    
                                } else if( event.isControlDown()) {
    
                                    if( selectionModel.contains( node)) {
                                        selectionModel.remove( node);
                                    } else {
                                        selectionModel.add( node);
                                    }
                                } else {
                                    selectionModel.add( node);
                                }
    
                            }
                        }
    
                    }
    
                    selectionModel.log();
    
                    rect.setX(0);
                    rect.setY(0);
                    rect.setWidth(0);
                    rect.setHeight(0);
    
                    group.getChildren().remove( rect);
    
                    event.consume();
    
                    enabled = false;
                }
            };
    
            EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
    
                @Override
                public void handle(MouseEvent event) {
    
                    double offsetX = event.getSceneX() - dragContext.mouseAnchorX;
                    double offsetY = event.getSceneY() - dragContext.mouseAnchorY;
    
                    if( offsetX > 0)
                        rect.setWidth( offsetX);
                    else {
                        rect.setX(event.getSceneX());
                        rect.setWidth(dragContext.mouseAnchorX - rect.getX());
                    }
    
                    if( offsetY > 0) {
                        rect.setHeight( offsetY);
                    } else {
                        rect.setY(event.getSceneY());
                        rect.setHeight(dragContext.mouseAnchorY - rect.getY());
                    }
    
                    event.consume();
    
                }
            };
    
            private final class DragContext {
    
                public double mouseAnchorX;
                public double mouseAnchorY;
    
    
            }
        }
    
    
        public static void main(String[] args) {
            launch(args);
        }
    
    }
    

    application.css

    .highlight {
        -fx-effect: dropshadow(three-pass-box, red, 4, 4, 0, 0);
    }
    

    屏幕截图:

    这篇关于实现拖动多个选定节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆