Java FX橡皮带调整错误

时间:2015-12-31 14:10:46

标签: java javafx rubber-band

所以,我的橡皮筋课上有一个错误,我似乎无法修复它。

我基本上做的是: 我有一个borderpane,它是我要调整大小的节点的外部窗格。我为此borderpane指定了一个宽度为1像素的边框(view css)。我还指定了这个边框4个矩形,每个矩形都在一个角落(NE,SE,SW,NW)。在这个边框中,我有所谓的内容手册'。此窗格包含所有内容(矩形,图像视图等)。

效果很好,但我似乎无法修复错误。

错误:

如果我调整一个像素的大小,则宽度和高度/ x和y将使用未知值进行调整。之后,调整大小正常。 enter image description here

RubberBand2.java

isFrozen()

的style.css

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);
        }
    }

}

Test2.java

.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;
}

当然,非常感谢任何建议和改进。

编辑: 对不起,我的文档已经过时了xD。

感谢。

2 个答案:

答案 0 :(得分:2)

好吧,我找到了解决问题的廉价方法。

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

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),如果没有,我可以帮助你。

答案 1 :(得分:1)

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

代码:

Main.java:创建节点,区域,形状并使它们可拖动和可调整大小

xargs -0 git update-index --no-skip-worktree

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

git update-index --no-skip-worktree

SelectionModel.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);
    }

}

SelectionOverlay.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);

    }

}

application.css

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);
        }
    }
}

截图:

enter image description here

ps:如果有人找到重构代码的时间,请分享