Java FX 橡皮筋调整大小错误
Java FX rubberband resize bug
所以,我的橡皮筋 class 有一个错误,我似乎无法修复它。
我基本上做的是:
我有一个 borderpane,它是我要调整大小的节点的外部窗格。我为这个 borderpane 分配了一个宽度为 1 像素的边框(视图 css)。我还为这个边框分配了 4 个矩形,每个矩形位于一个角(NE、SE、SW、NW)。在这个边框窗格中,我有所谓的 'contentPane'。此窗格包含所有内容(矩形、图像视图等)。
它工作得很好,但我似乎无法修复错误。
错误:
如果我调整一个像素的大小,宽度和 height/x 以及 y 将被调整为未知值。之后,调整大小就可以正常工作了。
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。
谢谢。
如果您有兴趣,这里有另一个版本。我前段时间做过这个,也许对你有用。我正在使用图层系统,我。 e.可调整大小的对象位于图层上,选择手柄位于对象图层顶部的单独图层上。
代码:
Main.java:创建节点、区域、形状并使它们可拖动和调整大小
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;
}
}
application.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;
}
以及截图:
ps:如果有人有时间重构代码,请分享
好吧,我找到了解决我的问题的便宜方法。
首先,我移动的是边框而不是它自己的节点,所以我改变了这个
nodeX = node.getBoundsInParent().getMinX();
nodeY = node.getBoundsInParent().getMinY();
对此
nodeX = parent.getBoundsInParent().getMinX();
nodeY = parent.getBoundsInParent().getMinY();
便宜的修复如下:
parent.setLayoutX(newX);
parent.setLayoutY(newY);
我只是根据角的大小增加 newX 和 newY。这让一切都完美无缺。
final double offset = RECTANGLE_CORNER_SIZE - 1;
parent.setLayoutX(newX + offset);
parent.setLayoutY(newY + offset);
我想你可以自己添加移动和其他调整大小的矩形(N、E、S、W),如果没有,我可以帮你。
所以,我的橡皮筋 class 有一个错误,我似乎无法修复它。
我基本上做的是: 我有一个 borderpane,它是我要调整大小的节点的外部窗格。我为这个 borderpane 分配了一个宽度为 1 像素的边框(视图 css)。我还为这个边框分配了 4 个矩形,每个矩形位于一个角(NE、SE、SW、NW)。在这个边框窗格中,我有所谓的 'contentPane'。此窗格包含所有内容(矩形、图像视图等)。
它工作得很好,但我似乎无法修复错误。
错误:
如果我调整一个像素的大小,宽度和 height/x 以及 y 将被调整为未知值。之后,调整大小就可以正常工作了。
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。
谢谢。
如果您有兴趣,这里有另一个版本。我前段时间做过这个,也许对你有用。我正在使用图层系统,我。 e.可调整大小的对象位于图层上,选择手柄位于对象图层顶部的单独图层上。
代码:
Main.java:创建节点、区域、形状并使它们可拖动和调整大小
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;
}
}
application.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;
}
以及截图:
ps:如果有人有时间重构代码,请分享
好吧,我找到了解决我的问题的便宜方法。
首先,我移动的是边框而不是它自己的节点,所以我改变了这个
nodeX = node.getBoundsInParent().getMinX();
nodeY = node.getBoundsInParent().getMinY();
对此
nodeX = parent.getBoundsInParent().getMinX();
nodeY = parent.getBoundsInParent().getMinY();
便宜的修复如下:
parent.setLayoutX(newX);
parent.setLayoutY(newY);
我只是根据角的大小增加 newX 和 newY。这让一切都完美无缺。
final double offset = RECTANGLE_CORNER_SIZE - 1;
parent.setLayoutX(newX + offset);
parent.setLayoutY(newY + offset);
我想你可以自己添加移动和其他调整大小的矩形(N、E、S、W),如果没有,我可以帮你。