2015-12-31 27 views
4

所以,我在我的橡皮筋班有一个错误,我似乎无法修复它。Java FX橡皮筋调整大小错误

我基本上做的是: 我有一个边框窗口,它是我想要调整大小的节点的外部窗格。我给这个边框分配一个宽度为1像素的边框(view css)。我还将这个边框分配了4个矩形,每个矩形位于一个角落(NE,SE,SW,NW)。在这个边界框中我有所谓的'contentPane'。该窗格包含所有内容(矩形,图像视图等)。

它工作得很好,但我似乎无法修复一个错误。

缺陷:

如果我调整一个像素,宽度和高度/ x和y被具有未知值的调整。之后,调整大小工作得很好。 enter image description here

RubberBand2.java

package org.displee.javafx.mod; 

import javafx.scene.Cursor; 
import javafx.scene.input.MouseEvent; 
import javafx.scene.layout.Pane; 
import javafx.scene.paint.Color; 
import javafx.scene.shape.Rectangle; 

/** 
* A class representing a rubberband that can be used to resize panes. 
* @author Displee 
*/ 
public class RubberBand2 { 

    /** 
    * The parent of this rectangle. 
    */ 
    private final Pane parent; 

    /** 
    * The rectangle. 
    */ 
    private final Pane node; 

    /** 
    * The corner rectangles. 
    */ 
    private final Rectangle[] rectangles = new Rectangle[4]; 

    /** 
    * The last known node width. 
    */ 
    private double nodeWidth; 

    /** 
    * The last known node height. 
    */ 
    private double nodeHeight; 

    /** 
    * The last known node x-coordinate. 
    */ 
    private double nodeX; 

    /** 
    * The last known node y-coordinate. 
    */ 
    private double nodeY; 

    /** 
    * The current selected component. 
    */ 
    public static RubberBand2 SELECTED; 

    /** 
    * The size of the corners of a rectangle. 
    */ 
    public static final double RECTANGLE_CORNER_SIZE = 5.0; 

    /** 
    * The minimum width of the {@code node}. 
    */ 
    private static final double MIN_WIDTH = 10.0; 

    /** 
    * The minimum height of the {@code node} 
    */ 
    private static final double MIN_HEIGHT = 10.0; 

    public RubberBand2(Pane node) { 
     this.node = node; 
     this.parent = (Pane) node.getParent(); 
     parent.getStyleClass().add("transparent_border"); 
     createCorners(); 
     bind(); 
    } 

    /** 
    * Create the corners. 
    */ 
    public void createCorners() { 
     final Pane inheritPane = node; 
     for (int i = 0; i < rectangles.length; i++) { 
      final Rectangle rectangle = rectangles[i] = new Rectangle(); 
      rectangle.setWidth(RECTANGLE_CORNER_SIZE); 
      rectangle.setHeight(RECTANGLE_CORNER_SIZE); 
      rectangle.setFill(Color.BLACK); 
      rectangle.getStyleClass().add("rectangle_corner"); 
      rectangle.setArcHeight(4); 
      rectangle.setArcWidth(4); 
      if (i == 0) { 
       rectangle.xProperty().bind(inheritPane.layoutXProperty().subtract(RECTANGLE_CORNER_SIZE)); 
       rectangle.yProperty().bind(inheritPane.layoutYProperty().subtract(RECTANGLE_CORNER_SIZE)); 
       rectangle.setOnMouseEntered((e) -> { 
        rectangle.setCursor(Cursor.NW_RESIZE); 
       }); 
       rectangle.setOnMouseDragged((event) -> { 
        resize(event, 0); 
       }); 
      } else if (i == 1) { 
       rectangle.xProperty().bind(inheritPane.layoutXProperty().add(inheritPane.widthProperty())); 
       rectangle.yProperty().bind(inheritPane.layoutYProperty().subtract(RECTANGLE_CORNER_SIZE)); 
       rectangle.setOnMouseEntered((e) -> { 
        rectangle.setCursor(Cursor.NE_RESIZE); 
       }); 
       rectangle.setOnMouseDragged((event) -> { 
        resize(event, 1); 
       }); 
      } else if (i == 2) { 
       rectangle.xProperty().bind(inheritPane.layoutXProperty().add(inheritPane.widthProperty())); 
       rectangle.yProperty().bind(inheritPane.layoutYProperty().add(inheritPane.heightProperty())); 
       rectangle.setOnMouseEntered((e) -> { 
        rectangle.setCursor(Cursor.SE_RESIZE); 
       }); 
       rectangle.setOnMouseDragged((event) -> { 
        resize(event, 2); 
       }); 
      } else { 
       rectangle.xProperty().bind(inheritPane.layoutXProperty().subtract(RECTANGLE_CORNER_SIZE)); 
       rectangle.yProperty().bind(inheritPane.layoutYProperty().add(inheritPane.heightProperty())); 
       rectangle.setOnMouseEntered((e) -> { 
        rectangle.setCursor(Cursor.SW_RESIZE); 
       }); 
       rectangle.setOnMouseDragged((event) -> { 
        resize(event, 3); 
       }); 
      } 
      rectangle.setOnMousePressed((e) -> { 
       setDefaults(); 
       e.consume(); 
      }); 
      rectangle.setVisible(false); 
      parent.getChildren().add(rectangle); 
     } 
    } 

    /** 
    * Bind the mouse events. 
    */ 
    public void bind() { 
     node.setOnMouseEntered((e) -> { 
      node.setCursor(Cursor.MOVE); 
     }); 
     parent.setOnMouseClicked((e) -> { 
      if (SELECTED != null) { 
       SELECTED.setRubberBandSelection(false); 
      } 
      SELECTED = this; 
      setRubberBandSelection(true); 
      e.consume(); 
     }); 
     node.setOnMouseClicked((e) -> { 
      if (SELECTED != null) { 
       SELECTED.setRubberBandSelection(false); 
      } 
      SELECTED = this; 
      setRubberBandSelection(true); 
      e.consume(); 
     }); 
     node.setOnMousePressed((e) -> { 
      setDefaults(); 
      e.consume(); 
     }); 
     node.setOnMouseMoved((e) -> { 

     }); 
     node.setOnMouseReleased((e) -> { 

     }); 
    } 

    /** 
    * Resize the argued resize type. 
    * @param event The mouse event 
    * @param type The type (0 = NW, 1 = NE, 2 = SE, 3 = SW); 
    */ 
    public void resize(MouseEvent event, int type) { 
     final double mouseX = parent.getBoundsInParent().getMinX() + event.getX(); 
     final double mouseY = parent.getBoundsInParent().getMinY() + event.getY(); 
     double newX = nodeX; 
     double newY = nodeY; 
     double newW = nodeWidth; 
     double newH = nodeHeight; 
     switch (type) { 
     case 0: 
      newX = mouseX; 
      newY = mouseY; 
      newW = nodeWidth + nodeX - newX; 
      newH = nodeHeight + nodeY - newY; 
      break; 
     case 1: 
      newY = mouseY; 
      newW = mouseX - nodeX; 
      newH = nodeHeight + nodeY - newY; 
      break; 
     case 2: 
      newW = mouseX - nodeX; 
      newH = mouseY - nodeY; 
      break; 
     case 3: 
      newX = mouseX; 
      newW = nodeWidth + nodeX - newX; 
      newH = mouseY - nodeY; 
      break; 
     } 
     parent.setLayoutX(newX); 
     parent.setLayoutY(newY); 
     node.setPrefSize(newW, newH); 
    } 

    /** 
    * Set the defaults before we resize anything. 
    */ 
    public void setDefaults() { 
     nodeX = node.getBoundsInParent().getMinX(); 
     nodeY = node.getBoundsInParent().getMinY(); 
     nodeHeight = node.getBoundsInParent().getHeight(); 
     nodeWidth = node.getBoundsInParent().getWidth(); 
    } 

    /** 
    * Set the rubber band selection for the rectangle. 
    * @param show If we have to show the corner rectangles. 
    */ 
    public void setRubberBandSelection(boolean show) { 
     if (show) { 
      parent.getStyleClass().remove("transparent_border"); 
      parent.getStyleClass().add("dotted_pane"); 
     } else { 
      parent.getStyleClass().remove("dotted_pane"); 
      parent.getStyleClass().add("transparent_border"); 
     } 
     for (Rectangle rectangle : rectangles) { 
      rectangle.setVisible(show); 
     } 
    } 

} 

的style.css

.dotted_pane { 
    -fx-background-insets: 0; 
    -fx-border-color: white; 
    -fx-border-width: 1; 
    -fx-border-style: dashed; 
} 
.transparent_border { 
    -fx-background-insets: 0; 
    -fx-border-color: transparent; 
    -fx-border-width: 1; 
    -fx-border-style: solid; 
} 

Test2.java

package org.test; 

import org.displee.javafx.mod.RubberBand2; 

import javafx.application.Application; 
import javafx.geometry.Insets; 
import javafx.scene.Group; 
import javafx.scene.Scene; 
import javafx.scene.layout.Background; 
import javafx.scene.layout.BackgroundFill; 
import javafx.scene.layout.Border; 
import javafx.scene.layout.BorderPane; 
import javafx.scene.layout.BorderStroke; 
import javafx.scene.layout.BorderStrokeStyle; 
import javafx.scene.layout.BorderWidths; 
import javafx.scene.layout.CornerRadii; 
import javafx.scene.layout.Pane; 
import javafx.scene.paint.Color; 
import javafx.stage.Stage; 

public class Test2 extends Application { 

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

    @Override 
    public void start(Stage primaryStage) throws Exception { 
     Group root = new Group(); 
     Scene scene = new Scene(root, 900, 600); 
     Pane inner = new Pane(); 
     inner.setBackground(new Background(new BackgroundFill(Color.web("red"), CornerRadii.EMPTY, Insets.EMPTY))); 
     BorderPane border = new BorderPane(inner); 
     inner.setPrefSize(100, 100); 
     border.setBorder(new Border(new BorderStroke(Color.BLUE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT))); 
     Pane outer = new Pane(border); 
     outer.layoutXProperty().bind(scene.widthProperty().divide(4)); 
     outer.layoutYProperty().bind(scene.heightProperty().divide(4)); 
     root.getChildren().addAll(outer); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
     new RubberBand2(inner); 
    } 

} 

任何建议和改进当然赞赏。

编辑: 对不起,我的文档是相当过时的xD。

谢谢。

+1

我可能是错的(不知道我是否正确地按照代码),但在'setDefaults'中,不应该'nodeHeight'和'nodeWidth'从'node.getBoundsInParent'而不是'parent.getBoundsInParent'计算? – Itai

+0

对不起,确实如此。我忘了把它改回来。该错误仍然出现。而且,这种改变只会让它变得更糟。 – Displee

回答

1

好了,我已经找到了便宜修复我的问题。

首先,我移动borderpane而不是节点它的自我,所以我改变了这个

nodeX = node.getBoundsInParent().getMinX(); 
nodeY = node.getBoundsInParent().getMinY(); 

对此

nodeX = parent.getBoundsInParent().getMinX(); 
nodeY = parent.getBoundsInParent().getMinY(); 

便宜的修复是以下几点:

parent.setLayoutX(newX); 
parent.setLayoutY(newY); 

我只是增加了下一页末和newY取决于角落的大小。这使得一切都完美无瑕。

final double offset = RECTANGLE_CORNER_SIZE - 1; 
parent.setLayoutX(newX + offset); 
parent.setLayoutY(newY + offset); 

我想你可以添加移动和其他调整大小的矩形(N,E,S,W)你的自我,如果没有,我可以帮你。

+1

这是否适合你?当您为RECTANGLE_CORNER_SIZE使用较大的值时,仍然会看到跳转。顺便说一下,3的相关值如何? – Roland

+0

你说得对。 '3'不适用于每个角落大小。我在自己的应用程序中使用了4.0大小,对于混淆抱歉。我编辑了答案并发布了适用于所有拐角尺寸的代码。 – Displee

+0

谢谢。我前段时间做过这样的事情。如果您有兴趣,我将其作为附加答案发布。 – Roland

0

如果你有兴趣,这里有一个替代版本。我前段时间做过,也许对你有用。我正在使用图层系统,我。即可调整大小的对象位于图层上,选择手柄位于对象图层顶部的单独图层上。

代码:

Main.java:创建节点,区域,形状,使他们可拖动和resizable

import javafx.application.Application; 
import javafx.scene.Group; 
import javafx.scene.Scene; 
import javafx.scene.control.Label; 
import javafx.scene.control.TitledPane; 
import javafx.scene.layout.StackPane; 
import javafx.scene.paint.Color; 
import javafx.scene.shape.Rectangle; 
import javafx.stage.Stage; 

public class Main extends Application { 

    SelectionModel selectionModel; 
    MouseHandler mouseHandler; 

    @Override 
    public void start(Stage primaryStage) { 

     Group root = new Group(); 

     // object layer 
     Group objectLayer = new Group(); 
     root.getChildren().add(objectLayer); 

     // selection layer on top of object layer 
     Group selectionLayer = new Group(); 
     root.getChildren().add(selectionLayer); 

     selectionModel = new SelectionModel(selectionLayer); 
     mouseHandler = new MouseHandler(selectionModel); 

     Rectangle rect1 = new Rectangle(200,100); 
     rect1.setFill(Color.RED.deriveColor(1, 1, 1, 0.2)); 
     rect1.relocate(100,100); 
     mouseHandler.makeDraggable(rect1); 

     Rectangle rect2 = new Rectangle(200,100); 
     rect2.setFill(Color.AQUA.deriveColor(1, 1, 1, 0.2)); 
     rect2.relocate(300,300); 
     mouseHandler.makeDraggable(rect2); 

     TitledPane sampleNode = new TitledPane(); 
     sampleNode.setPrefHeight(100); 
     sampleNode.setPrefWidth(200); 
     sampleNode.relocate(400,200); 
     mouseHandler.makeDraggable(sampleNode); 

     StackPane sampleNode2 = new StackPane(); 
     sampleNode2.getChildren().add(new Label("I'm a StackPane")); 
     sampleNode2.setStyle("-fx-background-color: lightblue"); 
     sampleNode2.setPrefHeight(100); 
     sampleNode2.setPrefWidth(200); 
     sampleNode2.relocate(600,300); 
     mouseHandler.makeDraggable(sampleNode2); 

     objectLayer.getChildren().addAll(rect1, rect2, sampleNode, sampleNode2); 

     Scene scene = new Scene(root, 1600, 900); 

     // clear selection when user clicks outside of cell 
     scene.setOnMousePressed(mouseEvent -> selectionModel.clear()); 

     scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); 

     primaryStage.setScene(scene); 
     primaryStage.show(); 

    } 

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

} 

MouseHandler.java:添加节点选择模型,允许拖动

import javafx.scene.Node; 

public class MouseHandler { 

    private SelectionModel selectionModel; 

    public MouseHandler(SelectionModel selectionModel) { 
     this.selectionModel = selectionModel; 
    } 

    private class DragContext { 
     double x; 
     double y; 
    } 

    public void makeDraggable(final Node node) { 

     final DragContext dragDelta = new DragContext(); 

     node.setOnMousePressed(mouseEvent -> { 

      // TODO: add shift & ctrl check to add/remove nodes to selection 
      selectionModel.clear(); 

      // add to selection model, create drag handles 
      selectionModel.add(node); 

      dragDelta.x = node.getTranslateX() - mouseEvent.getSceneX(); 
      dragDelta.y = node.getTranslateY() - mouseEvent.getSceneY(); 

      // consume event, so that scene won't get it (which clears selection) 
      mouseEvent.consume(); 
     }); 

     node.setOnMouseDragged(mouseEvent -> { 

      node.setTranslateX(mouseEvent.getSceneX() + dragDelta.x); 
      node.setTranslateY(mouseEvent.getSceneY() + dragDelta.y); 

     }); 

     node.setOnMouseReleased(mouseEvent -> { 

      fixPosition(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); 

    } 

} 

SelectionModel.java:将选定节点添加到选择模型中,为节点创建选择叠加层

import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 

import javafx.scene.Group; 
import javafx.scene.Node; 

public class SelectionModel { 

    Group selectionLayer; 
    Map<Node, SelectionOverlay> map = new HashMap<Node, SelectionOverlay>(); 

    List<Node> selection = new ArrayList<>(); 

    public SelectionModel(Group layoutBoundsOverlay) { 
     this.selectionLayer = layoutBoundsOverlay; 
    } 

    public void add(Node cell) { 

     // don't add duplicates or else duplicates might be added to the javafx scene graph which causes exceptions 
     if(selection.contains(cell)) 
      return; 

     SelectionOverlay selectionOverlay = new SelectionOverlay(cell); 

     // register 
     map.put(cell, selectionOverlay); 

     // add component 
     selectionLayer.getChildren().add(selectionOverlay); 

     selection.add(cell); 
    } 

    public void remove(Node cell) { 

     removeOverlay(cell); 

     selection.remove(cell); 

    } 

    private void removeOverlay(Node cell) { 
     SelectionOverlay boundsDisplay = map.get(cell); 
     if(boundsDisplay != null) { 
      selectionLayer.getChildren().remove(boundsDisplay); 
     } 
    } 

    public void clear() { 

     // remove style 
     // we can't call remove here because we'd get a ConcurrentModificationException (or we could use an iterator) 
     for(Node cell: selection) { 
      removeOverlay(cell); 
     } 

     // clear selection list 
     selection.clear(); 
    } 

    public boolean isEmpty() { 
     return selection.isEmpty(); 
    } 

    public boolean contains(Node cell) { 
     return selection.contains(cell); 
    } 


    public List<Node> getSelection() { 
     return selection; 
    } 

    public void log() { 
     for(Node task: selection) { 
      System.out.println("In selection: " + task); 
     } 
    } 
} 

SelectionOverlay.java:创建拖动手柄,每个拖动手柄显示光标,允许拖放使用拖放调整处理

import javafx.beans.property.ReadOnlyObjectProperty; 
import javafx.beans.value.ChangeListener; 
import javafx.beans.value.ObservableValue; 
import javafx.event.EventHandler; 
import javafx.geometry.Bounds; 
import javafx.scene.Cursor; 
import javafx.scene.Node; 
import javafx.scene.control.Control; 
import javafx.scene.input.MouseEvent; 
import javafx.scene.layout.Region; 
import javafx.scene.shape.Rectangle; 

public class SelectionOverlay extends Region { 

    /** 
    * Selection rectangle visibility 
    */ 
    boolean selectionRectangleVisible = true; 

    /** 
    * Drag handles visibility. In addition to this boolean the cell must implement ResizableI 
    */ 
    boolean dragHandlesVisible = true; 

    /** 
    * The shape (cell) to which the overlay has been assigned. 
    */ 
    final Node monitoredShape; 

    private ChangeListener<Bounds> boundsChangeListener; 

    /** 
    * Drag handle size 
    */ 
    double diameter = 6; 

    /** 
    * Drag handle half size, just to avoid/2.0 of radius everywhere 
    */ 
    double radius = diameter/2.0; 

    /** 
    * Selection rectangle around the shape/cell 
    */ 
    Rectangle selectionRectangle = new Rectangle(); 

    // Drag handles 
    DragHandle dragHandleNW; 
    DragHandle dragHandleNE; 
    DragHandle dragHandleSE; 
    DragHandle dragHandleSW; 
    DragHandle dragHandleN; 
    DragHandle dragHandleS; 
    DragHandle dragHandleE; 
    DragHandle dragHandleW; 

    Node cell; 

    public SelectionOverlay(final Node shape) { 

     this.cell = shape; 


     // mouse events only on our drag objects, but not on this node itself 
     // note that the selection rectangle is only for visuals and is set to being mouse transparent 
     setPickOnBounds(false); 

     // the rectangle is only for visuals, we don't want any mouse events on it 
     selectionRectangle.setMouseTransparent(true); 

     // drag handles: drag handles must be enabled AND the cell must implement ResizableI 
     dragHandlesVisible = dragHandlesVisible && (shape instanceof Node); 

     if(selectionRectangleVisible) { 

      // set style 
      selectionRectangle.getStyleClass().add("selection_rectangle"); 

      getChildren().add(selectionRectangle); 

     } 

     if(dragHandlesVisible) { 
      dragHandleNW = new DragHandle(diameter, Cursor.NW_RESIZE); 
      dragHandleNE = new DragHandle(diameter, Cursor.NE_RESIZE); 
      dragHandleSE = new DragHandle(diameter, Cursor.SE_RESIZE); 
      dragHandleSW = new DragHandle(diameter, Cursor.SW_RESIZE); 

      dragHandleN = new DragHandle(diameter, Cursor.N_RESIZE); 
      dragHandleS = new DragHandle(diameter, Cursor.S_RESIZE); 
      dragHandleE = new DragHandle(diameter, Cursor.E_RESIZE); 
      dragHandleW = new DragHandle(diameter, Cursor.W_RESIZE); 

      getChildren().addAll(dragHandleNW, dragHandleNE, dragHandleSE, dragHandleSW, dragHandleN, dragHandleS, dragHandleE, dragHandleW); 
     } 

     monitoredShape = shape; 

     monitorBounds(); 

    } 

    /** 
    * Set bounds listener for the overlay. 
    */ 
    private void monitorBounds() { 

     // determine the shape's 
     final ReadOnlyObjectProperty<Bounds> bounds = monitoredShape.boundsInParentProperty(); 

     // set the overlay based upon the new bounds and keep it in sync 
     updateBoundsDisplay(bounds.get()); 

     // keep the overlay based upon the new bounds in sync 
     boundsChangeListener = new ChangeListener<Bounds>() { 
     @Override public void changed(ObservableValue<? extends Bounds> observableValue, Bounds oldBounds, Bounds newBounds) { 
      updateBoundsDisplay(newBounds); 
     } 
     }; 

     bounds.addListener(boundsChangeListener); 
    } 

    /** 
    * Update this overlay to match a new set of bounds. 
    * @param newBounds 
    */ 
    private void updateBoundsDisplay(Bounds newBounds) { 

     if(selectionRectangleVisible) { 
      selectionRectangle.setX(newBounds.getMinX()); 
      selectionRectangle.setY(newBounds.getMinY()); 
      selectionRectangle.setWidth(newBounds.getWidth()); 
      selectionRectangle.setHeight(newBounds.getHeight()); 
     } 

     if(dragHandlesVisible) { 
      dragHandleNW.setX(newBounds.getMinX() - radius); 
      dragHandleNW.setY(newBounds.getMinY() - radius); 

      dragHandleNE.setX(newBounds.getMaxX() - radius); 
      dragHandleNE.setY(newBounds.getMinY() - radius); 

      dragHandleSE.setX(newBounds.getMaxX() - radius); 
      dragHandleSE.setY(newBounds.getMaxY() - radius); 

      dragHandleSW.setX(newBounds.getMinX() - radius); 
      dragHandleSW.setY(newBounds.getMaxY() - radius); 

      dragHandleN.setX(newBounds.getMinX() + newBounds.getWidth()/2.0 - radius); 
      dragHandleN.setY(newBounds.getMinY() - radius); 

      dragHandleS.setX(newBounds.getMinX() + newBounds.getWidth()/2.0 - radius); 
      dragHandleS.setY(newBounds.getMaxY() - radius); 

      dragHandleE.setX(newBounds.getMaxX() - radius); 
      dragHandleE.setY(newBounds.getMinY() + newBounds.getHeight()/2.0 - radius); 

      dragHandleW.setX(newBounds.getMinX() - radius); 
      dragHandleW.setY(newBounds.getMinY() + newBounds.getHeight()/2.0 - radius); 

     } 
    } 

    // make a node movable by dragging it around with the mouse. 
    private void enableDrag(final DragHandle dragHandle) { 

     final Delta dragDelta = new Delta(); 

     dragHandle.setOnMousePressed(new EventHandler<MouseEvent>() { 
      @Override 
      public void handle(MouseEvent mouseEvent) { 

       // record a delta distance for the drag and drop operation. 
       dragDelta.x = dragHandle.getX() - mouseEvent.getX(); 
       dragDelta.y = dragHandle.getY() - mouseEvent.getY(); 

       dragDelta.minX = cell.getBoundsInParent().getMinX(); 
       dragDelta.maxX = cell.getBoundsInParent().getMaxX(); 
       dragDelta.minY = cell.getBoundsInParent().getMinY(); 
       dragDelta.maxY = cell.getBoundsInParent().getMaxY(); 

       getScene().setCursor(dragHandle.getDragCursor()); 

       mouseEvent.consume(); 
      } 
     }); 
     dragHandle.setOnMouseReleased(new EventHandler<MouseEvent>() { 
      @Override 
      public void handle(MouseEvent mouseEvent) { 
       getScene().setCursor(Cursor.DEFAULT); 

       mouseEvent.consume(); 
      } 
     }); 
     dragHandle.setOnMouseDragged(new EventHandler<MouseEvent>() { 
      @Override 
      public void handle(MouseEvent mouseEvent) { 

       Node rCell = (Node) cell; 

       double newX = mouseEvent.getX() + dragDelta.x; 
       double newY = mouseEvent.getY() + dragDelta.y; 

       dragHandle.setX(newX); 
       dragHandle.setY(newY); 

       if(dragHandle == dragHandleN) { 

        setHeight(rCell, dragDelta.maxY - newY - radius); 
        rCell.relocate(dragDelta.minX, newY + radius); 

       } 
       else if(dragHandle == dragHandleNE) { 

        setWidth(rCell, newX - dragDelta.minX + radius); 
        setHeight(rCell, dragDelta.maxY - newY - radius); 
        rCell.relocate(dragDelta.minX, newY + radius); 

       } 
       else if(dragHandle == dragHandleE) { 

        setWidth(rCell, newX - dragDelta.minX + radius); 

       } 
       else if(dragHandle == dragHandleSE) { 

        setWidth(rCell, newX - dragDelta.minX + radius); 
        setHeight(rCell, newY - dragDelta.minY + radius); 

       } 
       else if(dragHandle == dragHandleS) { 

        setHeight(rCell, newY - dragDelta.minY + radius); 

       } 
       else if(dragHandle == dragHandleSW) { 

        setWidth(rCell, dragDelta.maxX - newX - radius); 
        setHeight(rCell, newY - dragDelta.minY + radius); 
        rCell.relocate(newX + radius, dragDelta.minY); 
       } 
       else if(dragHandle == dragHandleW) { 

        setWidth(rCell, dragDelta.maxX - newX - radius); 
        rCell.relocate(newX + radius, dragDelta.minY); 

       } 
       else if(dragHandle == dragHandleNW) { 

        setWidth(rCell, dragDelta.maxX - newX - radius); 
        setHeight(rCell, dragDelta.maxY - newY - radius); 
        rCell.relocate(newX + radius, newY + radius); 

       } 

       mouseEvent.consume(); 
      } 
     }); 
     dragHandle.setOnMouseEntered(new EventHandler<MouseEvent>() { 
      @Override 
      public void handle(MouseEvent mouseEvent) { 
       if (mouseEvent.isPrimaryButtonDown()) { 
        return; 
       } 

       getScene().setCursor(dragHandle.getDragCursor()); 

      } 
     }); 
     dragHandle.setOnMouseExited(new EventHandler<MouseEvent>() { 
      @Override 
      public void handle(MouseEvent mouseEvent) { 
       if (!mouseEvent.isPrimaryButtonDown()) { 
        getScene().setCursor(Cursor.DEFAULT); 
       } 
      } 
     }); 

    } 

    private void setWidth(Node node, double value) { 

     if(node instanceof Rectangle) { 

      Rectangle shape = (Rectangle) node; 
      shape.setWidth(value); 

     } else if (node instanceof Control) { 

      Control control = (Control) node; 
      control.setPrefWidth(value); 

     } else if (node instanceof Region) { 

      Region region = (Region) node; 
      region.setPrefWidth(value); 

     } 

    } 

    private void setHeight(Node node, double value) { 

     if(node instanceof Rectangle) { 

      Rectangle shape = (Rectangle) node; 
      shape.setHeight(value); 

     } else if (node instanceof Control) { 

      Control control = (Control) node; 
      control.setPrefHeight(value); 

     } else if (node instanceof Region) { 

      Region region = (Region) node; 
      region.setPrefHeight(value); 

     } 

    } 

    /** 
    * Drag handle 
    */ 
    private class DragHandle extends Rectangle { 

     Cursor dragCursor; 

     public DragHandle(double size, Cursor dragCursor) { 

      this.dragCursor = dragCursor; 

      setWidth(size); 
      setHeight(size); 

      getStyleClass().add("selection_drag_handle"); 

      enableDrag(this); 
     } 

     public Cursor getDragCursor() { 
      return dragCursor; 
     } 
    } 

    // records relative x and y co-ordinates. 
    private class Delta { 
     double x; 
     double y; 
     double minX; 
     double maxX; 
     double minY; 
     double maxY; 
    } 

    } 

应用。CSS

.selection_rectangle { 
    -fx-fill: transparent; 
    -fx-stroke: green; 
    -fx-stroke-width: 1; 
    -fx-stroke-dash-array: 2 4; 
    -fx-stroke-type: outside; 
} 
.selection_drag_handle { 
    -fx-fill: rgba(0, 128, 0, 0.6); /*green = #008000 */ 
    -fx-stroke: green; 
    -fx-stroke-width: 1; 
    -fx-stroke-type: outside; 
} 

而且截图:

enter image description here

PS:如果有人发现重构代码的时候,请不要共享