允许用户调整未修饰的舞台的大小

时间:2013-10-18 16:55:23

标签: java javafx-2 stage scene

我正在制作JavaFX中的屏幕录像机,并且屏幕录像机中必须使用的一个实用程序是让用户定义要记录的区域。

我设法做了一个未修饰的,半透明的Stage,可以拖动它来定义区域并添加一个close按钮让用户确认要记录的区域。

现在,如何让用户通过拖动边缘来调整舞台大小?

SSCCE:

package draggable;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.SceneBuilder;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBuilder;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.StackPaneBuilder;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class DraggableStage extends Application{

    Button close;
    StackPane holder;
    Rectangle2D maxBounds;
    Scene theScene;

    double pressedX;
    double pressedY;
    double draggedX;
    double draggedY;

    @Override
    public void start(Stage stage) throws Exception {
        final Stage theStage = stage;

        // determine how big the screen is
        maxBounds = Screen.getPrimary().getVisualBounds(); 

        //create the close button
        close = ButtonBuilder
                .create()
                .text("Close")
                .build();

        //create the StackPane holder for the button
        holder = StackPaneBuilder
                    .create()
                    .alignment(Pos.CENTER)
                    .children(close)
                    .build();

        // you cannot resize the screen beyond the max resolution of the screen
        holder.setMaxSize(maxBounds.getWidth(), maxBounds.getHeight());

        //you cannot resize under half the width and height of the screen
        holder.setMinSize(maxBounds.getWidth() / 2,maxBounds.getHeight() / 2);

        //the scene where it all happens
        theScene = SceneBuilder
                      .create()
                      .root(holder)
                      .width(maxBounds.getWidth() / 2)
                      .height(maxBounds.getHeight() / 2)
                      .build();

        // add the button listeners
        close.setOnAction(new EventHandler<ActionEvent>(){
            @Override
            public void handle(ActionEvent event) {
                theStage.close();
            }
        });

        // add the drag and press listener for the StackPane
        holder.setOnMousePressed(new EventHandler<MouseEvent>(){
            @Override
            public void handle(MouseEvent e) {
                pressedX = e.getX();
                pressedY = e.getY();
            }
        });

        holder.setOnMouseDragged(new EventHandler<MouseEvent>(){
            @Override
            public void handle(MouseEvent e) {
                draggedX = e.getX();
                draggedY = e.getY();

                double differenceX = draggedX - pressedX;
                double differenceY = draggedY - pressedY;

                theStage.setX(theStage.getX() + differenceX);
                theStage.setY(theStage.getY() + differenceY); 
            }
        });

        //the mandatory mumbo jumbo
        theScene.setFill(Color.rgb(128, 128, 128, 0.5));
        stage.initStyle(StageStyle.TRANSPARENT);
        stage.setScene(theScene);
        stage.sizeToScene();
        stage.show();
    }

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

图像:
enter image description here

13 个答案:

答案 0 :(得分:32)

我创建了一个ResizeHelper类,在这种情况下可以帮助你,用法:

ResizeHelper.addResizeListener(yourStage);

助手类:

import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;

//created by Alexander Berg
public class ResizeHelper {

    public static void addResizeListener(Stage stage) {
        ResizeListener resizeListener = new ResizeListener(stage);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_DRAGGED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener);
        ObservableList<Node> children = stage.getScene().getRoot().getChildrenUnmodifiable();
        for (Node child : children) {
            addListenerDeeply(child, resizeListener);
        }
    }

    public static void addListenerDeeply(Node node, EventHandler<MouseEvent> listener) {
        node.addEventHandler(MouseEvent.MOUSE_MOVED, listener);
        node.addEventHandler(MouseEvent.MOUSE_PRESSED, listener);
        node.addEventHandler(MouseEvent.MOUSE_DRAGGED, listener);
        node.addEventHandler(MouseEvent.MOUSE_EXITED, listener);
        node.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, listener);
        if (node instanceof Parent) {
            Parent parent = (Parent) node;
            ObservableList<Node> children = parent.getChildrenUnmodifiable();
            for (Node child : children) {
                addListenerDeeply(child, listener);
            }
        }
    }

    static class ResizeListener implements EventHandler<MouseEvent> {
        private Stage stage;
        private Cursor cursorEvent = Cursor.DEFAULT;
        private int border = 4;
        private double startX = 0;
        private double startY = 0;

        public ResizeListener(Stage stage) {
            this.stage = stage;
        }

        @Override
        public void handle(MouseEvent mouseEvent) {
            EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
            Scene scene = stage.getScene();

            double mouseEventX = mouseEvent.getSceneX(), 
                   mouseEventY = mouseEvent.getSceneY(),
                   sceneWidth = scene.getWidth(),
                   sceneHeight = scene.getHeight();

            if (MouseEvent.MOUSE_MOVED.equals(mouseEventType) == true) {
                if (mouseEventX < border && mouseEventY < border) {
                    cursorEvent = Cursor.NW_RESIZE;
                } else if (mouseEventX < border && mouseEventY > sceneHeight - border) {
                    cursorEvent = Cursor.SW_RESIZE;
                } else if (mouseEventX > sceneWidth - border && mouseEventY < border) {
                    cursorEvent = Cursor.NE_RESIZE;
                } else if (mouseEventX > sceneWidth - border && mouseEventY > sceneHeight - border) {
                    cursorEvent = Cursor.SE_RESIZE;
                } else if (mouseEventX < border) {
                    cursorEvent = Cursor.W_RESIZE;
                } else if (mouseEventX > sceneWidth - border) {
                    cursorEvent = Cursor.E_RESIZE;
                } else if (mouseEventY < border) {
                    cursorEvent = Cursor.N_RESIZE;
                } else if (mouseEventY > sceneHeight - border) {
                    cursorEvent = Cursor.S_RESIZE;
                } else {
                    cursorEvent = Cursor.DEFAULT;
                }
                scene.setCursor(cursorEvent);
            } else if(MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)){
                scene.setCursor(Cursor.DEFAULT);
            } else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType) == true) {
                startX = stage.getWidth() - mouseEventX;
                startY = stage.getHeight() - mouseEventY;
            } else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) == true) {
                if (Cursor.DEFAULT.equals(cursorEvent) == false) {
                    if (Cursor.W_RESIZE.equals(cursorEvent) == false && Cursor.E_RESIZE.equals(cursorEvent) == false) {
                        double minHeight = stage.getMinHeight() > (border*2) ? stage.getMinHeight() : (border*2);
                        if (Cursor.NW_RESIZE.equals(cursorEvent) == true || Cursor.N_RESIZE.equals(cursorEvent) == true || Cursor.NE_RESIZE.equals(cursorEvent) == true) {
                            if (stage.getHeight() > minHeight || mouseEventY < 0) {
                                stage.setHeight(stage.getY() - mouseEvent.getScreenY() + stage.getHeight());
                                stage.setY(mouseEvent.getScreenY());
                            }
                        } else {
                            if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) {
                                stage.setHeight(mouseEventY + startY);
                            }
                        }
                    }

                    if (Cursor.N_RESIZE.equals(cursorEvent) == false && Cursor.S_RESIZE.equals(cursorEvent) == false) {
                        double minWidth = stage.getMinWidth() > (border*2) ? stage.getMinWidth() : (border*2);
                        if (Cursor.NW_RESIZE.equals(cursorEvent) == true || Cursor.W_RESIZE.equals(cursorEvent) == true || Cursor.SW_RESIZE.equals(cursorEvent) == true) {
                            if (stage.getWidth() > minWidth || mouseEventX < 0) {
                                stage.setWidth(stage.getX() - mouseEvent.getScreenX() + stage.getWidth());
                                stage.setX(mouseEvent.getScreenX());
                            }
                        } else {
                            if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) {
                                stage.setWidth(mouseEventX + startX);
                            }
                        }
                    }
                }

            }
        }
    }
}

答案 1 :(得分:3)

以下是@ Alexander.Berg发布的ResizeHelper的更新版本,它支持最小和最大舞台大小。

package sem.helper;

import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;

/**
 * Util class to handle window resizing when a stage style set to StageStyle.UNDECORATED.
 * Created on 8/15/17.
 *
 * @author Evgenii Kanivets
 */
public class ResizeHelper {

    public static void addResizeListener(Stage stage) {
        addResizeListener(stage, 0, 0, Double.MAX_VALUE, Double.MAX_VALUE);
    }

    public static void addResizeListener(Stage stage, double minWidth, double minHeight, double maxWidth, double maxHeight) {
        ResizeListener resizeListener = new ResizeListener(stage);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_DRAGGED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener);

        resizeListener.setMinWidth(minWidth);
        resizeListener.setMinHeight(minHeight);
        resizeListener.setMaxWidth(maxWidth);
        resizeListener.setMaxHeight(maxHeight);

        ObservableList<Node> children = stage.getScene().getRoot().getChildrenUnmodifiable();
        for (Node child : children) {
            addListenerDeeply(child, resizeListener);
        }
    }

    private static void addListenerDeeply(Node node, EventHandler<MouseEvent> listener) {
        node.addEventHandler(MouseEvent.MOUSE_MOVED, listener);
        node.addEventHandler(MouseEvent.MOUSE_PRESSED, listener);
        node.addEventHandler(MouseEvent.MOUSE_DRAGGED, listener);
        node.addEventHandler(MouseEvent.MOUSE_EXITED, listener);
        node.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, listener);
        if (node instanceof Parent) {
            Parent parent = (Parent) node;
            ObservableList<Node> children = parent.getChildrenUnmodifiable();
            for (Node child : children) {
                addListenerDeeply(child, listener);
            }
        }
    }

    static class ResizeListener implements EventHandler<MouseEvent> {
        private Stage stage;
        private Cursor cursorEvent = Cursor.DEFAULT;
        private int border = 4;
        private double startX = 0;
        private double startY = 0;

        // Max and min sizes for controlled stage
        private double minWidth;
        private double maxWidth;
        private double minHeight;
        private double maxHeight;

        public ResizeListener(Stage stage) {
            this.stage = stage;
        }

        public void setMinWidth(double minWidth) {
            this.minWidth = minWidth;
        }

        public void setMaxWidth(double maxWidth) {
            this.maxWidth = maxWidth;
        }

        public void setMinHeight(double minHeight) {
            this.minHeight = minHeight;
        }

        public void setMaxHeight(double maxHeight) {
            this.maxHeight = maxHeight;
        }

        @Override
        public void handle(MouseEvent mouseEvent) {
            EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
            Scene scene = stage.getScene();

            double mouseEventX = mouseEvent.getSceneX(),
                    mouseEventY = mouseEvent.getSceneY(),
                    sceneWidth = scene.getWidth(),
                    sceneHeight = scene.getHeight();

            if (MouseEvent.MOUSE_MOVED.equals(mouseEventType)) {
                if (mouseEventX < border && mouseEventY < border) {
                    cursorEvent = Cursor.NW_RESIZE;
                } else if (mouseEventX < border && mouseEventY > sceneHeight - border) {
                    cursorEvent = Cursor.SW_RESIZE;
                } else if (mouseEventX > sceneWidth - border && mouseEventY < border) {
                    cursorEvent = Cursor.NE_RESIZE;
                } else if (mouseEventX > sceneWidth - border && mouseEventY > sceneHeight - border) {
                    cursorEvent = Cursor.SE_RESIZE;
                } else if (mouseEventX < border) {
                    cursorEvent = Cursor.W_RESIZE;
                } else if (mouseEventX > sceneWidth - border) {
                    cursorEvent = Cursor.E_RESIZE;
                } else if (mouseEventY < border) {
                    cursorEvent = Cursor.N_RESIZE;
                } else if (mouseEventY > sceneHeight - border) {
                    cursorEvent = Cursor.S_RESIZE;
                } else {
                    cursorEvent = Cursor.DEFAULT;
                }
                scene.setCursor(cursorEvent);
            } else if (MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)) {
                scene.setCursor(Cursor.DEFAULT);
            } else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) {
                startX = stage.getWidth() - mouseEventX;
                startY = stage.getHeight() - mouseEventY;
            } else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType)) {
                if (!Cursor.DEFAULT.equals(cursorEvent)) {
                    if (!Cursor.W_RESIZE.equals(cursorEvent) && !Cursor.E_RESIZE.equals(cursorEvent)) {
                        double minHeight = stage.getMinHeight() > (border * 2) ? stage.getMinHeight() : (border * 2);
                        if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.N_RESIZE.equals(cursorEvent)
                                || Cursor.NE_RESIZE.equals(cursorEvent)) {
                            if (stage.getHeight() > minHeight || mouseEventY < 0) {
                                setStageHeight(stage.getY() - mouseEvent.getScreenY() + stage.getHeight());
                                stage.setY(mouseEvent.getScreenY());
                            }
                        } else {
                            if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) {
                                setStageHeight(mouseEventY + startY);
                            }
                        }
                    }

                    if (!Cursor.N_RESIZE.equals(cursorEvent) && !Cursor.S_RESIZE.equals(cursorEvent)) {
                        double minWidth = stage.getMinWidth() > (border * 2) ? stage.getMinWidth() : (border * 2);
                        if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.W_RESIZE.equals(cursorEvent)
                                || Cursor.SW_RESIZE.equals(cursorEvent)) {
                            if (stage.getWidth() > minWidth || mouseEventX < 0) {
                                setStageWidth(stage.getX() - mouseEvent.getScreenX() + stage.getWidth());
                                stage.setX(mouseEvent.getScreenX());
                            }
                        } else {
                            if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) {
                                setStageWidth(mouseEventX + startX);
                            }
                        }
                    }
                }

            }
        }

        private void setStageWidth(double width) {
            width = Math.min(width, maxWidth);
            width = Math.max(width, minWidth);
            stage.setWidth(width);
        }

        private void setStageHeight(double height) {
            height = Math.min(height, maxHeight);
            height = Math.max(height, minHeight);
            stage.setHeight(height);
        }

    }
}

答案 2 :(得分:2)

我更新了@ Yevhenii.Kanivets的@ Alexander.Berg的ResizeHelper类的更新版本。这包括拖动场景的能力,以及我发现的ResizeHelper的最佳实现。还要将最小舞台大小设置为1的高度和1的宽度,而不是0,因为如果它们停留在0,则存在错误。

仍然有人可以改善这个答案。尽管这是个小问题,但SW,W,NW,N和NE边缘的大小调整不平滑。从这些边缘调整大小时,场景也可能会稍微移动。它们应像S,SE和E边缘一样平滑地调整大小。后面的边缘不会移动场景。

  1. @Cripperz的拖拽实现仍然存在错误。
  2. @Alessandro Roaro与上述问题相同。
  3. @Valeriy Blyus与上述问题相同。
  4. @Joseph Adomatis的实现不是通用的。

我不能代表@ M.K的实现,因为我没有使用Kotlin,但是如果您愿意,我会先给您一个镜头。

import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;

/**
 * Util class to handle window resizing when a stage style set to StageStyle.UNDECORATED.
 * Includes dragging of the stage.
 * Original on 6/13/14.
 * Updated on 8/15/17.
 * Updated on 12/19/19.
 *
 * @author Alexander.Berg
 * @author Evgenii Kanivets
 * @author Zachary Perales
 */

public class ResizeHelper {

    public static void addResizeListener(Stage stage) {
        addResizeListener(stage, 1, 1, Double.MAX_VALUE, Double.MAX_VALUE);
    }

    public static void addResizeListener(Stage stage, double minWidth, double minHeight, double maxWidth, double maxHeight) {
        ResizeListener resizeListener = new ResizeListener(stage);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_DRAGGED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener);

        resizeListener.setMinWidth(minWidth);
        resizeListener.setMinHeight(minHeight);
        resizeListener.setMaxWidth(maxWidth);
        resizeListener.setMaxHeight(maxHeight);

        ObservableList<Node> children = stage.getScene().getRoot().getChildrenUnmodifiable();
        for (Node child : children) {
            addListenerDeeply(child, resizeListener);
        }
    }

    private static void addListenerDeeply(Node node, EventHandler<MouseEvent> listener) {
        node.addEventHandler(MouseEvent.MOUSE_MOVED, listener);
        node.addEventHandler(MouseEvent.MOUSE_PRESSED, listener);
        node.addEventHandler(MouseEvent.MOUSE_DRAGGED, listener);
        node.addEventHandler(MouseEvent.MOUSE_EXITED, listener);
        node.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, listener);
        if (node instanceof Parent) {
            Parent parent = (Parent) node;
            ObservableList<Node> children = parent.getChildrenUnmodifiable();
            for (Node child : children) {
                addListenerDeeply(child, listener);
            }
        }
    }

    static class ResizeListener implements EventHandler<MouseEvent> {
        private Stage stage;
        private Cursor cursorEvent = Cursor.DEFAULT;
        private boolean resizing = true;
        private int border = 4;
        private double startX = 0;
        private double startY = 0;
        private double screenOffsetX = 0;
        private double screenOffsetY = 0;

        // Max and min sizes for controlled stage
        private double minWidth;
        private double maxWidth;
        private double minHeight;
        private double maxHeight;

        public ResizeListener(Stage stage) {
            this.stage = stage;
        }

        public void setMinWidth(double minWidth) {
            this.minWidth = minWidth;
        }

        public void setMaxWidth(double maxWidth) {
            this.maxWidth = maxWidth;
        }

        public void setMinHeight(double minHeight) {
            this.minHeight = minHeight;
        }

        public void setMaxHeight(double maxHeight) {
            this.maxHeight = maxHeight;
        }

        @Override
        public void handle(MouseEvent mouseEvent) {
            EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
            Scene scene = stage.getScene();

            double mouseEventX = mouseEvent.getSceneX(),
                    mouseEventY = mouseEvent.getSceneY(),
                    sceneWidth = scene.getWidth(),
                    sceneHeight = scene.getHeight();

            if (MouseEvent.MOUSE_MOVED.equals(mouseEventType)) {
                if (mouseEventX < border && mouseEventY < border) {
                    cursorEvent = Cursor.NW_RESIZE;
                } else if (mouseEventX < border && mouseEventY > sceneHeight - border) {
                    cursorEvent = Cursor.SW_RESIZE;
                } else if (mouseEventX > sceneWidth - border && mouseEventY < border) {
                    cursorEvent = Cursor.NE_RESIZE;
                } else if (mouseEventX > sceneWidth - border && mouseEventY > sceneHeight - border) {
                    cursorEvent = Cursor.SE_RESIZE;
                } else if (mouseEventX < border) {
                    cursorEvent = Cursor.W_RESIZE;
                } else if (mouseEventX > sceneWidth - border) {
                    cursorEvent = Cursor.E_RESIZE;
                } else if (mouseEventY < border) {
                    cursorEvent = Cursor.N_RESIZE;
                } else if (mouseEventY > sceneHeight - border) {
                    cursorEvent = Cursor.S_RESIZE;
                } else {
                    cursorEvent = Cursor.DEFAULT;
                }
                scene.setCursor(cursorEvent);
            } else if (MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)) {
                scene.setCursor(Cursor.DEFAULT);
            } else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) {
                startX = stage.getWidth() - mouseEventX;
                startY = stage.getHeight() - mouseEventY;
            } else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType)) {
                if (!Cursor.DEFAULT.equals(cursorEvent)) {
                    resizing = true;
                    if (!Cursor.W_RESIZE.equals(cursorEvent) && !Cursor.E_RESIZE.equals(cursorEvent)) {
                        double minHeight = stage.getMinHeight() > (border * 2) ? stage.getMinHeight() : (border * 2);
                        if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.N_RESIZE.equals(cursorEvent)
                                || Cursor.NE_RESIZE.equals(cursorEvent)) {
                            if (stage.getHeight() > minHeight || mouseEventY < 0) {
                                setStageHeight(stage.getY() - mouseEvent.getScreenY() + stage.getHeight());
                                stage.setY(mouseEvent.getScreenY() );
                            }
                        } else {
                            if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) {
                                setStageHeight(mouseEventY + startY);
                            }
                        }
                    }

                    if (!Cursor.N_RESIZE.equals(cursorEvent) && !Cursor.S_RESIZE.equals(cursorEvent)) {
                        double minWidth = stage.getMinWidth() > (border * 2) ? stage.getMinWidth() : (border * 2);
                        if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.W_RESIZE.equals(cursorEvent)
                                || Cursor.SW_RESIZE.equals(cursorEvent)) {
                            if (stage.getWidth() > minWidth || mouseEventX < 0) {
                                setStageWidth(stage.getX() - mouseEvent.getScreenX() + stage.getWidth());
                                stage.setX(mouseEvent.getScreenX());
                            }
                        } else {
                            if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) {
                                setStageWidth(mouseEventX + startX);
                            }
                        }
                    }
                    resizing = false;
                }
            }

            if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType) && Cursor.DEFAULT.equals(cursorEvent)) {
                resizing = false;
                screenOffsetX = stage.getX() - mouseEvent.getScreenX();
                screenOffsetY = stage.getY() - mouseEvent.getScreenY();
            }

            if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) && Cursor.DEFAULT.equals(cursorEvent) && resizing == false) {
                stage.setX(mouseEvent.getScreenX() + screenOffsetX);
                stage.setY(mouseEvent.getScreenY() + screenOffsetY);
            }

        }

        private void setStageWidth(double width) {
            width = Math.min(width, maxWidth);
            width = Math.max(width, minWidth);
            stage.setWidth(width);
        }

        private void setStageHeight(double height) {
            height = Math.min(height, maxHeight);
            height = Math.max(height, minHeight);
            stage.setHeight(height);
        }

    }
}

答案 3 :(得分:1)

这是@ Alexander.Berg的帖子的修订版本。这样可以正确处理场景的minWidth和maxWidth属性。他的版本没有这样做:它完全忽略了maxWidth和屠夫minWidth。底部还有一段代码可以拖动窗口。

import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;

public class ResizeHelper {

    public static void addResizeListener(Stage stage) {
        ResizeListener resizeListener = new ResizeListener(stage);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_DRAGGED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener);
        stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener);
        ObservableList<Node> children = stage.getScene().getRoot().getChildrenUnmodifiable();
        for (Node child : children) {
            addListenerDeeply(child, resizeListener);
        }
    }

    private static void addListenerDeeply(Node node, EventHandler<MouseEvent> listener) {
        node.addEventHandler(MouseEvent.MOUSE_MOVED, listener);
        node.addEventHandler(MouseEvent.MOUSE_PRESSED, listener);
        node.addEventHandler(MouseEvent.MOUSE_DRAGGED, listener);
        node.addEventHandler(MouseEvent.MOUSE_EXITED, listener);
        node.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, listener);
        if (node instanceof Parent) {
            Parent parent = (Parent) node;
            ObservableList<Node> children = parent.getChildrenUnmodifiable();
            for (Node child : children) {
                addListenerDeeply(child, listener);
            }
        }
    }

    private static class ResizeListener implements EventHandler<MouseEvent> {
        private Stage stage;
        private Cursor cursorEvent = Cursor.DEFAULT;
        private int border = 4;
        private double startX = 0;
        private double startY = 0;
        private double startScreenX = 0;
        private double startScreenY = 0;

        public ResizeListener(Stage stage) {
            this.stage = stage;
        }

        @Override
        public void handle(MouseEvent mouseEvent) {
            EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
            Scene scene = stage.getScene();

            double mouseEventX = mouseEvent.getSceneX();
            double mouseEventY = mouseEvent.getSceneY();
            double sceneWidth = scene.getWidth();
            double sceneHeight = scene.getHeight();

            if (MouseEvent.MOUSE_MOVED.equals(mouseEventType)) {
                if (mouseEventX < border && mouseEventY < border) {
                    cursorEvent = Cursor.NW_RESIZE;
                } else if (mouseEventX < border && mouseEventY > sceneHeight - border) {
                    cursorEvent = Cursor.SW_RESIZE;
                } else if (mouseEventX > sceneWidth - border && mouseEventY < border) {
                    cursorEvent = Cursor.NE_RESIZE;
                } else if (mouseEventX > sceneWidth - border && mouseEventY > sceneHeight - border) {
                    cursorEvent = Cursor.SE_RESIZE;
                } else if (mouseEventX < border) {
                    cursorEvent = Cursor.W_RESIZE;
                } else if (mouseEventX > sceneWidth - border) {
                    cursorEvent = Cursor.E_RESIZE;
                } else if (mouseEventY < border) {
                    cursorEvent = Cursor.N_RESIZE;
                } else if (mouseEventY > sceneHeight - border) {
                    cursorEvent = Cursor.S_RESIZE;
                } else {
                    cursorEvent = Cursor.DEFAULT;
                }
                scene.setCursor(cursorEvent);
            } else if (MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)) {
                scene.setCursor(Cursor.DEFAULT);
            } else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) {
                startX = stage.getWidth() - mouseEventX;
                startY = stage.getHeight() - mouseEventY;
            } else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType)) {
                if (!Cursor.DEFAULT.equals(cursorEvent)) {
                    if (!Cursor.W_RESIZE.equals(cursorEvent) && !Cursor.E_RESIZE.equals(cursorEvent)) {
                        double minHeight = stage.getMinHeight() > (border * 2) ? stage.getMinHeight() : (border * 2);
                        double maxHeight = stage.getMaxHeight();
                        if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.N_RESIZE.equals(cursorEvent) || Cursor.NE_RESIZE.equals(cursorEvent)) {
                            double newHeight = stage.getHeight() - (mouseEvent.getScreenY() - stage.getY());
                            if (newHeight >= minHeight && newHeight <= maxHeight) {
                                stage.setHeight(newHeight);
                                stage.setY(mouseEvent.getScreenY());
                            } else {
                                newHeight = Math.min(Math.max(newHeight, minHeight), maxHeight);
                                // y1 + h1 = y2 + h2
                                // y1 = y2 + h2 - h1
                                stage.setY(stage.getY() + stage.getHeight() - newHeight);
                                stage.setHeight(newHeight);
                            }
                        } else {
                            stage.setHeight(Math.min(Math.max(mouseEventY + startY, minHeight), maxHeight));
                        }
                    }

                    if (!Cursor.N_RESIZE.equals(cursorEvent) && !Cursor.S_RESIZE.equals(cursorEvent)) {
                        double minWidth = stage.getMinWidth() > (border * 2) ? stage.getMinWidth() : (border * 2);
                        double maxWidth = stage.getMaxWidth();
                        if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.W_RESIZE.equals(cursorEvent) || Cursor.SW_RESIZE.equals(cursorEvent)) {
                            double newWidth = stage.getWidth() - (mouseEvent.getScreenX() - stage.getX());
                            if (newWidth >= minWidth && newWidth <= maxWidth) {
                                stage.setWidth(newWidth);
                                stage.setX(mouseEvent.getScreenX());
                            } else {
                                newWidth = Math.min(Math.max(newWidth, minWidth), maxWidth);
                                // x1 + w1 = x2 + w2
                                // x1 = x2 + w2 - w1
                                stage.setX(stage.getX() + stage.getWidth() - newWidth);
                                stage.setWidth(newWidth);
                            }
                        } else {
                            stage.setWidth(Math.min(Math.max(mouseEventX + startX, minWidth), maxWidth));
                        }
                    }
                }
            }

            if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) {
                startScreenX = mouseEvent.getScreenX();
                startScreenY = mouseEvent.getScreenY();
            } else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType)) {
                if (Cursor.DEFAULT.equals(cursorEvent)) {
                    stage.setX(stage.getX() + mouseEvent.getScreenX() - startScreenX);
                    startScreenX = mouseEvent.getScreenX();
                    stage.setY(stage.getY() + mouseEvent.getScreenY() - startScreenY);
                    startScreenY = mouseEvent.getScreenY();
                }
            }
        }
    }
}

答案 4 :(得分:0)

在我尝试“解决”它之前,我已经研究了一些讨论它的线程,但最终没有问题。

我在右下角创建了一个按钮,给Button一个OnDraggedMethod并简单地使用了mouseevent并在MousePosition上设置了Height / Width - 阶段X / Y位置。

    double newX = event.getScreenX() - stage.getX() + 13;
    double newY = event.getScreenY() - stage.getY() + 10;
    if (newX % 5 == 0 || newY % 5 == 0) {
        if (newX > 550) {
            stage.setWidth(newX);
        } else {
            stage.setWidth(550);
        }

        if (newY > 200) {
            stage.setHeight(newY);
        } else {
            stage.setHeight(200);
        }
    }

另外,我设置了最小宽度和高度。 并且因为我认为滞后调整动画很烦人我用if包围声明并且每隔5个像素调整一次舞台。

看起来好多了!

PS:+13和+10仅适用于MousePosition。其他鼠标是在角落而不是按钮^^。

答案 5 :(得分:0)

我能说什么......我是一个学习缓慢的人。我花了一些时间来仔细阅读所有代码并理解它。它并不难......只是发生了很多事情。我决定进行一些更改以使用绝对鼠标位置而不是相对鼠标位置,我还改变了整体类使用静态成员和诸如此类的东西。这就是我想出来的,对我自己来说效果很好。对于那些寻找类似解决方案的人,请享受我丰富的评论......使用此类的此类版本非常简单:

ResizeListener listener = new ResizeListener(stage);

NVL2( string1, value_if_not_null, value_if_null )

答案 6 :(得分:0)

以下是 @ Alexander.Berg 发布的ResizeHelper的更新版本,它支持窗口拖动

import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;

//created by Alexander Berg
public class ResizeHelper {
    private static double xOffset = 0; 
    private static double yOffset = 0;

public static void addResizeListener(Stage stage) {
    ResizeListener resizeListener = new ResizeListener(stage);
    stage.getScene().addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener);
    stage.getScene().addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener);
    stage.getScene().addEventHandler(MouseEvent.MOUSE_DRAGGED, resizeListener);
    stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener);
    stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener);

    ObservableList<Node> children = stage.getScene().getRoot().getChildrenUnmodifiable();
    for (Node child : children) {
        addListenerDeeply(child, resizeListener);
    }
}

public static void addListenerDeeply(Node node, EventHandler<MouseEvent> listener) {
    node.addEventHandler(MouseEvent.MOUSE_MOVED, listener);
    node.addEventHandler(MouseEvent.MOUSE_PRESSED, listener);
    node.addEventHandler(MouseEvent.MOUSE_DRAGGED, listener);
    node.addEventHandler(MouseEvent.MOUSE_EXITED, listener);
    node.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, listener);
    if (node instanceof Parent) {
        Parent parent = (Parent) node;
        ObservableList<Node> children = parent.getChildrenUnmodifiable();
        for (Node child : children) {
            addListenerDeeply(child, listener);
        }
    }
}

static class ResizeListener implements EventHandler<MouseEvent> {
    private Stage stage;
    private Cursor cursorEvent = Cursor.DEFAULT;
    private int border = 4;
    private double startX = 0;
    private double startY = 0;

    public ResizeListener(Stage stage) {
        this.stage = stage;
    }

    @Override
    public void handle(MouseEvent mouseEvent) {
        EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
        Scene scene = stage.getScene();

        double mouseEventX = mouseEvent.getSceneX(), 
               mouseEventY = mouseEvent.getSceneY(),
               sceneWidth = scene.getWidth(),
               sceneHeight = scene.getHeight();

        if (MouseEvent.MOUSE_MOVED.equals(mouseEventType) == true) {
            if (mouseEventX < border && mouseEventY < border) {
                cursorEvent = Cursor.NW_RESIZE;
            } else if (mouseEventX < border && mouseEventY > sceneHeight - border) {
                cursorEvent = Cursor.SW_RESIZE;
            } else if (mouseEventX > sceneWidth - border && mouseEventY < border) {
                cursorEvent = Cursor.NE_RESIZE;
            } else if (mouseEventX > sceneWidth - border && mouseEventY > sceneHeight - border) {
                cursorEvent = Cursor.SE_RESIZE;
            } else if (mouseEventX < border) {
                cursorEvent = Cursor.W_RESIZE;
            } else if (mouseEventX > sceneWidth - border) {
                cursorEvent = Cursor.E_RESIZE;
            } else if (mouseEventY < border) {
                cursorEvent = Cursor.N_RESIZE;
            } else if (mouseEventY > sceneHeight - border) {
                cursorEvent = Cursor.S_RESIZE;
            } else {
                cursorEvent = Cursor.DEFAULT;
            }
            scene.setCursor(cursorEvent);
        } else if(MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)){
            scene.setCursor(Cursor.DEFAULT);
        } else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType) == true) {
            startX = stage.getWidth() - mouseEventX;
            startY = stage.getHeight() - mouseEventY;
            xOffset = mouseEvent.getSceneX();
            yOffset = mouseEvent.getSceneY();

        } else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) == true) {
            if (Cursor.DEFAULT.equals(cursorEvent) == false) {
                if (Cursor.W_RESIZE.equals(cursorEvent) == false && Cursor.E_RESIZE.equals(cursorEvent) == false) {
                    double minHeight = stage.getMinHeight() > (border*2) ? stage.getMinHeight() : (border*2);
                    if (Cursor.NW_RESIZE.equals(cursorEvent) == true || Cursor.N_RESIZE.equals(cursorEvent) == true || Cursor.NE_RESIZE.equals(cursorEvent) == true) {
                        if (stage.getHeight() > minHeight || mouseEventY < 0) {
                            stage.setHeight(stage.getY() - mouseEvent.getScreenY() + stage.getHeight());
                            stage.setY(mouseEvent.getScreenY());
                        }
                    } else {
                        if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) {
                            stage.setHeight(mouseEventY + startY);
                        }
                    }
                }

                if (Cursor.N_RESIZE.equals(cursorEvent) == false && Cursor.S_RESIZE.equals(cursorEvent) == false) {
                    double minWidth = stage.getMinWidth() > (border*2) ? stage.getMinWidth() : (border*2);
                    if (Cursor.NW_RESIZE.equals(cursorEvent) == true || Cursor.W_RESIZE.equals(cursorEvent) == true || Cursor.SW_RESIZE.equals(cursorEvent) == true) {
                        if (stage.getWidth() > minWidth || mouseEventX < 0) {
                            stage.setWidth(stage.getX() - mouseEvent.getScreenX() + stage.getWidth());
                            stage.setX(mouseEvent.getScreenX());
                        }
                    } else {
                        if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) {
                            stage.setWidth(mouseEventX + startX);
                        }
                    }
                }
            }
            else if (mouseEvent.getSceneY() < 70) {
                stage.setX(mouseEvent.getScreenX() - xOffset);
                stage.setY(mouseEvent.getScreenY() - yOffset);
            }

        }
    }
}

}

答案 7 :(得分:0)

Thanks @Alexander.Berg for providing this utility class (1st version). It helped me to get the desired functionality for my Undecorated Stage.

However, I have few queries regarding some points:

  1. What is need to traverse through all the child nodes and set the handler to each and every parent node. Is is not sufficient to put the handler on Scene only?
  2. If we are anyway checking for the EventType in handler, what is need in setting the same handler to different event types. Can it be set on one super mouse event (MouseEvent.ANY) and can skip the rest.
  3. If point#1 is handled, then there is no need to consider MOUSE_EXITED and MOUSE_EXITED_TARGET to set the default cursor back.
  4. There is every possible chance to trigger the event on underlying node(s) in the drag space(border space) if the handler is added as event handler. The most common case can be if the custom stage close button is very near to corner which comes under the border space. Trying to resize at that space will eventually trigger the close button. Should it need to be implement by adding it as filter (and consume the event when neccessary) rather than handler?

I tried to modify your code to address all the above queries. Please find below the updated ResizeHelper. I am only trying to simplify the things a bit further. Once again thanks for providing the code and letting me to think with a starting point.

import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;

/**
 * Helper class to set the resizing implementation for any given undecorated stage.
 */
public class ResizeHelper {

    /**
     * Handler to process the resizing of the the given stage.
     */
    static class ResizeHandler implements EventHandler<MouseEvent> {

        /** Space to consider around the stage border for resizing */
        private static final int BORDER = 10;

        /** Stage to which the handler is implemented */
        private final Stage stage;

        /** Current cursor reference to the scene */
        private Cursor cursor = Cursor.DEFAULT;

        /** X position of the drag start */
        private double startX = 0;

        /** Y position of the drag start */
        private double startY = 0;

        /**
         * Constructor.
         *
         * @param stageTmp Stage to which resizing to be set.
         */
        public ResizeHandler(final Stage stageTmp) {
            stage = stageTmp;
        }

        @Override
        public void handle(final MouseEvent event) {
            final EventType<? extends MouseEvent> eventType = event.getEventType();
            final Scene scene = stage.getScene();
            final double mouseEventX = event.getSceneX();
            final double mouseEventY = event.getSceneY();
            final double sceneWidth = scene.getWidth();
            final double sceneHeight = scene.getHeight();

            if (MouseEvent.MOUSE_MOVED.equals(eventType)) {
                setCursor(mouseEventX, mouseEventY, sceneWidth, sceneHeight);
                scene.setCursor(cursor);

            } else if (MouseEvent.MOUSE_PRESSED.equals(eventType)) {
                startX = stage.getWidth() - mouseEventX;
                startY = stage.getHeight() - mouseEventY;
                consumeEventIfNotDefaultCursor(event);

            } else if (MouseEvent.MOUSE_DRAGGED.equals(eventType) && !Cursor.DEFAULT.equals(cursor)) {
                consumeEventIfNotDefaultCursor(event);
                if (!Cursor.W_RESIZE.equals(cursor) && !Cursor.E_RESIZE.equals(cursor)) {
                    processVerticalDrag(event);
                }

                if (!Cursor.N_RESIZE.equals(cursor) && !Cursor.S_RESIZE.equals(cursor)) {
                    processHorizontalDrag(event);
                }
            }
        }

        /**
         * Consumes the mouse event if the cursor is not the DEFAULT cursor.
         *
         * @param event MouseEvent instance.
         */
        private void consumeEventIfNotDefaultCursor(final MouseEvent event) {
            if (!cursor.equals(Cursor.DEFAULT)) {
                event.consume();
            }
        }

        /**
         * Processes the horizontal drag movement and resizes the window width.
         *
         * @param event MouseEvent instance.
         */
        private void processHorizontalDrag(final MouseEvent event) {
            final double minWidth =
                    stage.getMinWidth() > BORDER * 2 ? stage.getMinWidth() : BORDER * 2;
            final double mouseEventX = event.getSceneX();
            if (Cursor.NW_RESIZE.equals(cursor)
                || Cursor.W_RESIZE.equals(cursor)
                || Cursor.SW_RESIZE.equals(cursor)) {
                if (stage.getWidth() > minWidth || mouseEventX < 0) {
                    stage.setWidth(stage.getX() - event.getScreenX() + stage.getWidth());
                    stage.setX(event.getScreenX());
                }
            } else if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) {
                stage.setWidth(mouseEventX + startX);
            }
        }

        /**
         * Processes the vertical drag movement and resizes the window height.
         *
         * @param event MouseEvent instance.
         */
        private void processVerticalDrag(final MouseEvent event) {
            final double minHeight =
                    stage.getMinHeight() > BORDER * 2 ? stage.getMinHeight() : BORDER * 2;
            final double mouseEventY = event.getSceneY();
            if (Cursor.NW_RESIZE.equals(cursor)
                || Cursor.N_RESIZE.equals(cursor)
                || Cursor.NE_RESIZE.equals(cursor)) {
                if (stage.getHeight() > minHeight || mouseEventY < 0) {
                    stage.setHeight(stage.getY() - event.getScreenY() + stage.getHeight());
                    stage.setY(event.getScreenY());
                }
            } else if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) {
                stage.setHeight(mouseEventY + startY);
            }
        }

        /**
         * Determines and sets the appropriate cursor based on the mouse position in relative to scene bounds.
         *
         * @param mouseEventX X position of mouse in the scene.
         * @param mouseEventY Y position of mouse in the scene.
         * @param sceneWidth Width of the scene.
         * @param sceneHeight Height of the scene.
         */
        private void setCursor(final double mouseEventX, final double mouseEventY, final double sceneWidth,
                final double sceneHeight) {
            final Cursor cursor1;
            if (mouseEventX < BORDER && mouseEventY < BORDER) {
                cursor1 = Cursor.NW_RESIZE;
            } else if (mouseEventX < BORDER && mouseEventY > sceneHeight - BORDER) {
                cursor1 = Cursor.SW_RESIZE;
            } else if (mouseEventX > sceneWidth - BORDER && mouseEventY < BORDER) {
                cursor1 = Cursor.NE_RESIZE;
            } else if (mouseEventX > sceneWidth - BORDER && mouseEventY > sceneHeight - BORDER) {
                cursor1 = Cursor.SE_RESIZE;
            } else if (mouseEventX < BORDER) {
                cursor1 = Cursor.W_RESIZE;
            } else if (mouseEventX > sceneWidth - BORDER) {
                cursor1 = Cursor.E_RESIZE;
            } else if (mouseEventY < BORDER) {
                cursor1 = Cursor.N_RESIZE;
            } else if (mouseEventY > sceneHeight - BORDER) {
                cursor1 = Cursor.S_RESIZE;
            } else {
                cursor1 = Cursor.DEFAULT;
            }
            cursor = cursor1;
        }
    }

    /**
     * Constructor.
     *
     */
    private ResizeHelper() {

    }

    /**
     * Adds the resize handler to the provided stage.
     *
     * @param stage Stage to which the resizing should be implemented.
     */
    public static void addResizeHandler(final Stage stage) {
        ResizeHandler resizeHandler = new ResizeHandler(stage);
        stage.setMinHeight(stage.getHeight());
        stage.setMinWidth(stage.getWidth());
        stage.setMaxHeight(stage.getMaxHeight());
        stage.setMaxWidth(stage.getMaxWidth());
        stage.getScene().addEventFilter(MouseEvent.ANY, resizeHandler);
    }
}

答案 8 :(得分:0)

科林版本

受此处众多答案的启发,我编写了自己的实现,该实现具有我能想到的所有必要功能:

功能

  • 可拖动性

    • 窗口可拖动
    • 可定制的交互区域(顶部,右侧,底部,左侧)
    • 将鼠标悬停在拖动区域上时自定义光标
  • 全屏

    • 可以将窗口设置为全屏
    • 窗口将恢复到以前的大小和位置
    • 可定制的交互区域(顶部,右侧,底部,左侧)
  • 可调整大小

    • 可以通过窗口边框在任何方向(n,ne,e,se,s,se,w,nw)调整窗口大小
    • 可定制的交互区域(n,e,w,s)
    • 双击窗口边框将调整窗口大小并占用该方向上的所有可用空间
    • 将鼠标悬停在调整大小区域上时,会显示自定义光标

用法

快速设置:

StageInteractor(this)
    .makeDraggable()
    .makeFullscreenable()
    .makeResizable()

自定义交互区域:

StageInteractor(this)

    // custom width / height for drag areas (top, right, bottom, left)
    .makeDraggable(50, 20, 50, 20) 

    // custom width / height for double-clickable areas (top, right, bottom, left)
    .makeFullscreenable(50, 20, 50, 20)

    // custom width / height for resize areas (top, right, bottom, left)
    .makeResizable(20, 20, 20, 20)

代码

import javafx.scene.Cursor
import javafx.scene.Scene
import javafx.scene.input.MouseButton
import javafx.scene.input.MouseEvent
import javafx.stage.Screen
import javafx.stage.Stage

class StageInteractor {
    private val stage: Stage
    private val scene: Scene

    private var isDraggable = false
    private var isDragging = false
    private var allowDragging = true
    private var dragMarginTop = 0.0
    private var dragMarginRight = 0.0
    private var dragMarginBottom = 0.0
    private var dragMarginLeft = 0.0

    private var isFullscreenable = false
    private var isFullscreen = false
    private var allowFullscreen = true
    private var fullscreenMarginTop = 0.0
    private var fullscreenMarginRight = 0.0
    private var fullscreenMarginBottom = 0.0
    private var fullscreenMarginLeft = 0.0
    private var stageWidthBeforeFullscreen = 0.0
    private var stageHeightBeforeFullscreen = 0.0
    private var stageXBeforeFullscreen = 0.0
    private var stageYBeforeFullscreen = 0.0

    private var isResizeable = false
    private var isResizing = false
    private var allowResizing = true
    private var resizeDirection: ResizeDirection? = null
    private var resizeMarginTop = 0.0
    private var resizeMarginRight = 0.0
    private var resizeMarginBottom = 0.0
    private var resizeMarginLeft = 0.0


    constructor(stage: Stage) {
        this.stage = stage
        this.scene = stage.scene
    }

    fun makeDraggable(
        marginTop: Double = 50.0,
        marginRight: Double = 0.0,
        marginBottom: Double = 0.0,
        marginLeft: Double = 0.0
    ): StageInteractor {
        dragMarginTop = marginTop
        dragMarginRight = marginRight
        dragMarginBottom = marginBottom
        dragMarginLeft = marginLeft

        if (!isDraggable) {
            isDraggable = true

            var dragStartOffsetX = 0.0
            var dragStartOffsetY = 0.0

            scene.addEventHandler(MouseEvent.MOUSE_MOVED) {
                val isWithinBounds = detectDraggingBounds(it)

                if (isDraggable && allowDragging && isWithinBounds) {
                    scene.cursor = Cursor.OPEN_HAND
                } else {
                    if (scene.cursor == Cursor.OPEN_HAND) {
                        scene.cursor = Cursor.DEFAULT
                    }
                }
            }

            scene.addEventHandler(MouseEvent.MOUSE_PRESSED) {
                dragStartOffsetX = stage.x - it.screenX
                dragStartOffsetY = stage.y - it.screenY
            }

            scene.addEventHandler(MouseEvent.MOUSE_DRAGGED) {
                val isWithinBounds = detectDraggingBounds(it)

                if (isDraggable && allowDragging && isWithinBounds) {
                    isDragging = true
                    scene.cursor = Cursor.CLOSED_HAND
                }

                if (isDragging) {
                    stage.x = it.screenX + dragStartOffsetX
                    stage.y = it.screenY + dragStartOffsetY
                }
            }

            scene.addEventHandler(MouseEvent.MOUSE_RELEASED) {
                if (isDragging) {
                    isDragging = false
                    scene.cursor = Cursor.DEFAULT
                }
            }
        }

        return this
    }

    private fun detectDraggingBounds(event: MouseEvent): Boolean {
        return event.sceneY <= dragMarginTop
                || scene.height - event.sceneY <= dragMarginBottom
                || event.sceneX <= dragMarginLeft
                || scene.width - event.sceneX <= dragMarginRight
    }

    fun makeFullscreenable(
        marginTop: Double = 50.0,
        marginRight: Double = 0.0,
        marginBottom: Double = 0.0,
        marginLeft: Double = 0.0
    ): StageInteractor {
        fullscreenMarginTop = marginTop
        fullscreenMarginRight = marginRight
        fullscreenMarginBottom = marginBottom
        fullscreenMarginLeft = marginLeft

        if (!isFullscreenable) {
            isFullscreenable = true

            scene.addEventHandler(MouseEvent.MOUSE_PRESSED) {
                val isDoubleClick = it.button == MouseButton.PRIMARY && it.clickCount >= 2

                if (isFullscreenable && allowFullscreen && isDoubleClick && detectFullscreenBounds(it)) {
                    if (isFullscreen) {
                        isFullscreen = false
                        allowDragging = true
                        allowResizing = true

                        stage.x = stageXBeforeFullscreen
                        stage.y = stageYBeforeFullscreen
                        stage.width = stageWidthBeforeFullscreen
                        stage.height = stageHeightBeforeFullscreen
                    } else {
                        isFullscreen = true
                        allowDragging = false
                        allowResizing = false
                        stageWidthBeforeFullscreen = stage.width
                        stageHeightBeforeFullscreen = stage.height
                        stageXBeforeFullscreen = stage.x
                        stageYBeforeFullscreen = stage.y

                        val screenBounds = Screen.getPrimary().visualBounds
                        val newWidth = if (stage.maxWidth < screenBounds.width) {
                            stage.maxWidth
                        } else {
                            screenBounds.width
                        }
                        val newHeight = if (stage.maxHeight < screenBounds.height) {
                            stage.maxHeight
                        } else {
                            screenBounds.height
                        }

                        stage.width = newWidth
                        stage.height = newHeight
                        stage.x = screenBounds.minX
                        stage.y = screenBounds.minY
                    }
                }
            }

        }

        return this
    }

    private fun detectFullscreenBounds(event: MouseEvent): Boolean {
        val isWithinBounds = event.sceneY <= fullscreenMarginTop
                || scene.height - event.sceneY <= fullscreenMarginBottom
                || event.sceneX <= fullscreenMarginLeft
                || scene.width - event.sceneX <= fullscreenMarginRight

        val resizeDirection = detectResizeDirection(event)

        return isWithinBounds && resizeDirection == null
    }

    fun makeResizable(
        marginTop: Double = 10.0,
        marginRight: Double = 10.0,
        marginBottom: Double = 10.0,
        marginLeft: Double = 10.0
    ): StageInteractor {
        resizeMarginTop = marginTop
        resizeMarginRight = marginRight
        resizeMarginBottom = marginBottom
        resizeMarginLeft = marginLeft

        if (!isResizeable) {
            isResizeable = true

            scene.addEventHandler(MouseEvent.MOUSE_MOVED) {
                if (isResizeable && allowResizing && !isResizing) {
                    when (detectResizeDirection(it)) {
                        ResizeDirection.NORTH_WEST -> scene.cursor = Cursor.NW_RESIZE
                        ResizeDirection.NORTH_EAST -> scene.cursor = Cursor.NE_RESIZE
                        ResizeDirection.SOUTH_WEST -> scene.cursor = Cursor.SW_RESIZE
                        ResizeDirection.SOUTH_EAST -> scene.cursor = Cursor.SE_RESIZE
                        ResizeDirection.NORTH -> scene.cursor = Cursor.N_RESIZE
                        ResizeDirection.SOUTH -> scene.cursor = Cursor.S_RESIZE
                        ResizeDirection.WEST -> scene.cursor = Cursor.W_RESIZE
                        ResizeDirection.EAST -> scene.cursor = Cursor.E_RESIZE
                        else -> {
                            val cursors = listOf(
                                Cursor.NW_RESIZE,
                                Cursor.NE_RESIZE,
                                Cursor.SW_RESIZE,
                                Cursor.SE_RESIZE,
                                Cursor.N_RESIZE,
                                Cursor.S_RESIZE,
                                Cursor.W_RESIZE,
                                Cursor.E_RESIZE
                            )

                            if (cursors.contains(scene.cursor)) {
                                scene.cursor = Cursor.DEFAULT
                            }
                        }
                    }
                }
            }

            var resizeStartFromSceneX = 0.0
            var resizeStartFromSceneY = 0.0
            var resizeStartFromScreenX = 0.0
            var resizeStartFromScreenY = 0.0
            var resizeStartStageWidth = 0.0
            var resizeStartStageHeight = 0.0

            scene.addEventHandler(MouseEvent.MOUSE_PRESSED) {
                if (isResizeable && allowResizing && !isResizing) {
                    resizeDirection = detectResizeDirection(it)

                    if (resizeDirection != null) {
                        if (it.button == MouseButton.PRIMARY && it.clickCount >= 2) {
                            val screenBounds = Screen.getPrimary().visualBounds

                            if (resizeDirection == ResizeDirection.NORTH || resizeDirection == ResizeDirection.NORTH_WEST || resizeDirection == ResizeDirection.NORTH_EAST) {
                                stage.height = ensureStageHeightIsWithinLimits(
                                    stage.height + stage.y - screenBounds.minY
                                )
                                stage.y = 0.0
                            }

                            if (resizeDirection == ResizeDirection.SOUTH || resizeDirection == ResizeDirection.SOUTH_WEST || resizeDirection == ResizeDirection.SOUTH_EAST) {
                                stage.height = ensureStageHeightIsWithinLimits(
                                    screenBounds.height - stage.y + screenBounds.minY
                                )

                                if (stage.height == screenBounds.height) {
                                    stage.y = 0.0
                                }
                            }

                            if (resizeDirection == ResizeDirection.WEST || resizeDirection == ResizeDirection.NORTH_WEST || resizeDirection == ResizeDirection.SOUTH_WEST) {
                                stage.width = ensureStageWidthIsWithinLimits(
                                    stage.width + stage.x
                                )
                                stage.x = 0.0
                            }

                            if (resizeDirection == ResizeDirection.EAST || resizeDirection == ResizeDirection.NORTH_EAST || resizeDirection == ResizeDirection.SOUTH_EAST) {
                                stage.width = ensureStageWidthIsWithinLimits(
                                    screenBounds.width - stage.x
                                )

                                if (stage.width == screenBounds.width) {
                                    stage.x = 0.0
                                }
                            }
                        } else {
                            isResizing = true
                            isDraggable = false
                            isFullscreenable = false

                            resizeStartFromScreenX = it.screenX
                            resizeStartFromScreenY = it.screenY
                            resizeStartFromSceneX = it.sceneX
                            resizeStartFromSceneY = it.sceneY
                            resizeStartStageWidth = stage.width
                            resizeStartStageHeight = stage.height
                        }
                    }
                }
            }

            scene.addEventHandler(MouseEvent.MOUSE_DRAGGED) {
                if (isResizing) {
                    if (resizeDirection == ResizeDirection.NORTH || resizeDirection == ResizeDirection.NORTH_WEST || resizeDirection == ResizeDirection.NORTH_EAST) {
                        val newHeight = ensureStageHeightIsWithinLimits(
                            resizeStartStageHeight + (resizeStartFromScreenY - it.screenY)
                        )
                        val newY = when (newHeight) {
                            stage.maxHeight, stage.minHeight -> stage.y
                            else -> it.screenY - resizeStartFromSceneY
                        }

                        stage.height = newHeight
                        stage.y = newY
                    }

                    if (resizeDirection == ResizeDirection.SOUTH || resizeDirection == ResizeDirection.SOUTH_WEST || resizeDirection == ResizeDirection.SOUTH_EAST) {
                        val newHeight = ensureStageHeightIsWithinLimits(
                            resizeStartStageHeight + (it.screenY - resizeStartFromScreenY)
                        )

                        stage.height = newHeight
                    }

                    if (resizeDirection == ResizeDirection.WEST || resizeDirection == ResizeDirection.NORTH_WEST || resizeDirection == ResizeDirection.SOUTH_WEST) {
                        val newWidth = ensureStageWidthIsWithinLimits(
                            resizeStartStageWidth + (resizeStartFromScreenX - it.screenX)
                        )
                        val newX = when (newWidth) {
                            stage.maxWidth, stage.minWidth -> stage.x
                            else -> it.screenX - resizeStartFromSceneX
                        }

                        stage.width = newWidth
                        stage.x = newX
                    }

                    if (resizeDirection == ResizeDirection.EAST || resizeDirection == ResizeDirection.NORTH_EAST || resizeDirection == ResizeDirection.SOUTH_EAST) {
                        val newWidth = ensureStageWidthIsWithinLimits(
                            resizeStartStageWidth + (it.screenX - resizeStartFromScreenX)
                        )

                        stage.width = newWidth
                    }
                }
            }

            scene.addEventHandler(MouseEvent.MOUSE_RELEASED) {
                if (isResizing) {
                    isResizing = false
                    isDraggable = true
                    isFullscreenable = true
                }
            }
        }

        return this
    }

    private fun detectResizeDirection(event: MouseEvent): ResizeDirection? {
        val isNorthResize = event.sceneY <= resizeMarginTop
        val isSouthResize = scene.height - event.sceneY <= resizeMarginBottom
        val isWestResize = event.sceneX <= resizeMarginLeft
        val isEastResize = scene.width - event.sceneX <= resizeMarginRight
        val isNorthWestResize = isNorthResize && isWestResize
        val isNorthEastResize = isNorthResize && isEastResize
        val isSouthWestResize = isSouthResize && isWestResize
        val isSouthEastResize = isSouthResize && isEastResize

        return when {
            isNorthWestResize -> ResizeDirection.NORTH_WEST
            isNorthEastResize -> ResizeDirection.NORTH_EAST
            isSouthWestResize -> ResizeDirection.SOUTH_WEST
            isSouthEastResize -> ResizeDirection.SOUTH_EAST
            isNorthResize -> ResizeDirection.NORTH
            isSouthResize -> ResizeDirection.SOUTH
            isWestResize -> ResizeDirection.WEST
            isEastResize -> ResizeDirection.EAST
            else -> null
        }
    }

    private fun ensureStageWidthIsWithinLimits(width: Double): Double {
        val screenBounds = Screen.getPrimary().visualBounds

        return when {
            width > stage.maxWidth -> stage.maxWidth
            width < stage.minWidth -> stage.minWidth
            width > screenBounds.width -> screenBounds.width
            else -> width
        }
    }

    private fun ensureStageHeightIsWithinLimits(height: Double): Double {
        val screenBounds = Screen.getPrimary().visualBounds

        return when {
            height > stage.maxHeight -> stage.maxHeight
            height < stage.minHeight -> stage.minHeight
            height > screenBounds.height -> screenBounds.height
            else -> height
        }
    }

    enum class ResizeDirection {
        NORTH, NORTH_EAST, NORTH_WEST,
        SOUTH, SOUTH_EAST, SOUTH_WEST,
        EAST, WEST;
    }
}

答案 9 :(得分:0)

我在舞台的根容器中也使原始解决方案也支持填充。否则,光标将无法正确设置,并且大小也将无法调整。 通过在开始拖动时考虑到鼠标在舞台内的位置,避免在舞台重新定位时避免“跳” ,调整大小的效果也更好。

package apro2.canbustool.ui.util;

import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.Insets;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;

//created by Alexander Berg 
public class ResizeHelper {

public static ResizeListener addResizeListener (Stage stage) {
    ResizeListener resizeListener = new ResizeListener(stage);
    Scene scene = stage.getScene();
    scene.addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener);
    scene.addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener);
    scene.addEventHandler(MouseEvent.MOUSE_DRAGGED, resizeListener);
    scene.addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener);
    scene.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener);

    return resizeListener;
}


public static class ResizeListener implements EventHandler<MouseEvent> {
    private Stage stage;
    private Scene scene;
    private Cursor cursorEvent = Cursor.DEFAULT;
    private int border = 4;
    private double startX = 0;
    private double startY = 0;
    private double sceneOffsetX = 0;
    private double sceneOffsetY = 0;
    private double padTop = 0;
    private double padRight = 0;
    private double padBottom = 0;
    private double padLeft = 0;


    public ResizeListener (Stage stage) {
        this.stage = stage;
        this.scene = stage.getScene();
    }


    public void setPadding (Insets padding) {
        padTop = padding.getTop();
        padRight = padding.getRight();
        padBottom = padding.getBottom();
        padLeft = padding.getLeft();
    }


    @Override
    public void handle(MouseEvent mouseEvent) {
        EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();

        double mouseEventX = mouseEvent.getSceneX(),
                mouseEventY = mouseEvent.getSceneY(),
                viewWidth = stage.getWidth() - padLeft - padRight,
                viewHeight = stage.getHeight() - padTop - padBottom;

        if (MouseEvent.MOUSE_MOVED.equals(mouseEventType)) {
            if (mouseEventX < border + padLeft && mouseEventY < border + padTop) {
                cursorEvent = Cursor.NW_RESIZE;
            }
            else if (mouseEventX < border + padLeft && mouseEventY > viewHeight - border + padTop) {
                cursorEvent = Cursor.SW_RESIZE;
            }
            else if (mouseEventX > viewWidth - border + padLeft && mouseEventY < border + padTop) {
                cursorEvent = Cursor.NE_RESIZE;
            }
            else if (mouseEventX > viewWidth - border + padLeft && mouseEventY > viewHeight - border + padTop) {
                cursorEvent = Cursor.SE_RESIZE;
            }
            else if (mouseEventX < border + padLeft) {
                cursorEvent = Cursor.W_RESIZE;
            }
            else if (mouseEventX > viewWidth - border + padLeft) {
                cursorEvent = Cursor.E_RESIZE;
            }
            else if (mouseEventY < border + padTop) {
                cursorEvent = Cursor.N_RESIZE;
            }
            else if (mouseEventY > viewHeight - border + padTop) {
                cursorEvent = Cursor.S_RESIZE;
            }
            else {
                cursorEvent = Cursor.DEFAULT;
            }

            scene.setCursor(cursorEvent);
        }
        else if (MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)){
            scene.setCursor(Cursor.DEFAULT);
        }
        else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) {
            startX = viewWidth - mouseEventX;
            startY = viewHeight - mouseEventY;
            sceneOffsetX = mouseEvent.getSceneX();
            sceneOffsetY = mouseEvent.getSceneY();
        }
        else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) && !Cursor.DEFAULT.equals(cursorEvent)) {
            if (!Cursor.W_RESIZE.equals(cursorEvent) && !Cursor.E_RESIZE.equals(cursorEvent)) {
                double minHeight = stage.getMinHeight() > (border*2) ? stage.getMinHeight() : (border*2);

                if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.N_RESIZE.equals(cursorEvent) || Cursor.NE_RESIZE.equals(cursorEvent)) {
                    if (stage.getHeight() > minHeight || mouseEventY < 0) {
                        double height = stage.getY() - mouseEvent.getScreenY() + stage.getHeight() + sceneOffsetY;
                        double y = mouseEvent.getScreenY() - sceneOffsetY;

                        stage.setHeight(height);
                        stage.setY(y);
                    }
                } else {
                    if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) {
                        stage.setHeight(mouseEventY + startY + padBottom + padTop);
                    }
                }
            }

            if (!Cursor.N_RESIZE.equals(cursorEvent) && !Cursor.S_RESIZE.equals(cursorEvent)) {
                double minWidth = stage.getMinWidth() > (border*2) ? stage.getMinWidth() : (border*2);
                if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.W_RESIZE.equals(cursorEvent) || Cursor.SW_RESIZE.equals(cursorEvent)) {
                    if (stage.getWidth() > minWidth || mouseEventX < 0) {
                        double width = stage.getX() - mouseEvent.getScreenX() + stage.getWidth() + sceneOffsetX;
                        double x = mouseEvent.getScreenX() - sceneOffsetX;

                        stage.setWidth(width);
                        stage.setX(x);
                    }
                } else {
                    if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) {
                        stage.setWidth(mouseEventX + startX + padLeft + padRight);
                    }
                }
            }
        }
    }
}

}

答案 10 :(得分:0)

我知道这个线程很旧,但是我认为新人们正在为这个问题而绊脚石,上面提供的代码没有使真正的方法成为可能,我花了大约2天的时间使用上面的解决方案,并且不希望有完美的可拖动和可调整大小的窗口,直到现在。

https://github.com/goxr3plus/FX-BorderlessScene

该库使用3行代码设法使场景表现出了正确的行为,从而节省了我的理智。

答案 11 :(得分:0)

支持:

  1. 阶段调整大小,限制在最小和最大范围
  2. 舞台重新定位,在屏幕边界有限制
  3. 双击,一侧拉伸
  4. 光标由 javafx 系统自动设置,因此不太容易出现图形光标错误
  5. 缩进支持,可用于 CSS 阴影
  6. 干净、简短、快速、易读、适应性强的源代码,没有复杂的嵌套查询,因此是一种智能裁剪方法
  7. 无需深度注册Handler,节点从root继承游标 并且没有不必要地使用 MouseEvent.MOUSE_MOVED

构建概念:

使用 Alignment.TOP|RIGHT|BOTTOM|LEFTAlignment.TOP_LEFT|BOTTOM_LEFT 作为角将边框调整大小窗格放置在根 StackPane 内

Gluon GUI 设计器 FXML_Structure:

/** Copyright © 2021 Izon Company, Free To Share: class ResizeHelper.java */

import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.stage.Screen;
import javafx.stage.Stage;

/**
 * handles Stage resizing for StageStyle.UNDECORATED and deals with a
 * indentation which can be used to render a CSS drop shadow effect around the
 * scene with transparent background.
 * 
 * @author Henryk Zschuppan,
 * @date MARCH 01,21
 */

public class ResizeHandler implements EventHandler<MouseEvent> {

    public static ResizeHandler install(Stage stage, double titlebarHeight, double pullEdgeDepth, double indentation) {
        ResizeHandler handler = new ResizeHandler(stage, titlebarHeight, pullEdgeDepth, indentation);
        stage.getScene().addEventHandler(MouseEvent.ANY, handler);
        return handler;
    }

    public static Rectangle2D SCREEN_BOUNDS = Screen.getPrimary().getVisualBounds();

    /** select the boundary clipping orientation in relation to the stage */
    private static enum CHECK {
        LOW,
        HIGH,
        NONE;
    }

    /** Stage to which the handler is implemented */
    final private Stage stage;
    /** Area from top to consider for stage reposition */
    double titlebarHeight;
    /** Space to consider around the stage border for resizing */
    final private int depth;
    /** padding space to render in the CSS effect drop shadow */
    final private double pad;
    /** stage size limits */
    final private double minWidth, minHeight, maxWidth, maxHeight;
    /** start point of mouse position on screen */
    private Point2D startDrag = null;
    /** frame rectangle of the stage on drag start */
    private Rectangle2D startRectangle;
    /** the relative mouse orientation to the stage */
    private CHECK checkX = CHECK.NONE, checkY = CHECK.NONE;

    private boolean inRepositioningArea = false;

    private ResizeHandler(Stage stage, double titlebarHeight, double pullEdgeDepth, double indentation) {
        this.stage = stage;
        this.titlebarHeight = titlebarHeight;
        pad = indentation;
        depth = (int) (indentation + pullEdgeDepth);

        minWidth = stage.getMinWidth();
        minHeight = stage.getMinHeight();
        maxWidth = stage.getMaxWidth();
        maxHeight = stage.getMaxHeight();
    }

    @Override
    public void handle(MouseEvent mouseEvent) {
        if (!mouseEvent.getButton().equals(MouseButton.PRIMARY))
            return;

        EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
        final double mX = mouseEvent.getScreenX();
        final double mY = mouseEvent.getScreenY();
        /* local coordinates inside stage */
        final double lX = mouseEvent.getSceneX();
        final double lY = mouseEvent.getSceneY();
        final double sW = stage.getWidth();
        final double sH = stage.getHeight();

        if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) {
            if (lX < depth && lY < depth) {
                setXYCheck(CHECK.LOW, CHECK.LOW);
            } else if (lX < depth && lY > sH - depth) {
                setXYCheck(CHECK.LOW, CHECK.HIGH);
            } else if (lX > sW - depth && lY < depth) {
                setXYCheck(CHECK.HIGH, CHECK.LOW);
            } else if (lX > sW - depth && lY > sH - depth) {
                setXYCheck(CHECK.HIGH, CHECK.HIGH);
            } else if (lX < depth) {
                setXYCheck(CHECK.LOW, CHECK.NONE);
            } else if (lX > sW - depth) {
                setXYCheck(CHECK.HIGH, CHECK.NONE);
            } else if (lY < depth) {
                setXYCheck(CHECK.NONE, CHECK.LOW);
            } else if (lY > sH - depth) {
                setXYCheck(CHECK.NONE, CHECK.HIGH);
            } else {
                setXYCheck(CHECK.NONE, CHECK.NONE);
            }

            /* check mouse is not inside the resize border space */
            if (lX < pad || lY < pad || lX > sW - pad || lY > sH - pad) {
                setXYCheck(CHECK.NONE, CHECK.NONE);
            }

            inRepositioningArea = lY >= depth && lY < this.titlebarHeight + pad;

            startDrag = new Point2D(mX, mY);
            startRectangle = new Rectangle2D(stage.getX(), stage.getY(), sW, sH);

        } else if (!isNone() && MouseEvent.MOUSE_DRAGGED.equals(mouseEventType)) {
            /* stage resizing */
            double dX = mX - startDrag.getX();
            double dY = mY - startDrag.getY();
            double min, max;
            /* don't overwrite start values */
            double x = startRectangle.getMinX(), y = startRectangle.getMinY(), x2 = startRectangle.getMaxX(), y2 = startRectangle.getMaxY();

            switch (checkX) {
                case LOW :// LEFT
                    min = Math.max(x - maxWidth, (0 - pad));
                    max = x2 - minWidth;
                    x = clip(x + dX, min, max);
                    break;
                case HIGH : // RIGHT
                    min = x + minWidth;
                    max = Math.min(x + maxWidth, SCREEN_BOUNDS.getWidth() + pad);
                    x2 = clip(x2 + dX, min, max);
                default :
                    break;
            }

            switch (checkY) {
                case LOW : // TOP
                    min = Math.max(y2 - maxHeight, (0 - pad));
                    max = y2 - minHeight;
                    y = clip(y + dY, min, max);
                    break;
                case HIGH :// BOTTOM
                    min = y + minHeight;
                    max = Math.min(y + maxHeight, SCREEN_BOUNDS.getHeight() + pad);
                    y2 = clip(y2 + dY, min, max);
                default :
                    break;
            }

            updateStagePosition(x, y, x2, y2);

        } else if (isNone() && MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) && inRepositioningArea) {
            /* stage repositioning */
            double dX = mX - startDrag.getX();
            double dY = mY - startDrag.getY();

            this.stage.setX(startRectangle.getMinX() + dX);
            this.stage.setY(startRectangle.getMinY() + dY);
            stagePositionInsideScreen();

        } else if (!isNone() && MouseEvent.MOUSE_RELEASED.equals(mouseEventType) && mouseEvent.getClickCount() == 2) {
            /* The stage side is expanded or minimized by double-clicking */
            double min, max;
            /* don't overwrite start values */
            double x = startRectangle.getMinX(), y = startRectangle.getMinY(), x2 = startRectangle.getMaxX(), y2 = startRectangle.getMaxY();

            switch (checkX) {
                case LOW :// LEFT
                    if (x > (0 - pad)) {
                        min = Math.max(x - maxWidth, (0 - pad));
                        max = x2 - minWidth;
                        x = clip((0 - pad), min, max);
                    } else {
                        x = x2 - minWidth;
                    }
                    break;
                case HIGH : // RIGHT
                    if (x2 < SCREEN_BOUNDS.getWidth() + pad) {
                        min = x + minWidth;
                        max = Math.min(x + maxWidth, SCREEN_BOUNDS.getWidth() + pad);
                        x2 = clip(SCREEN_BOUNDS.getWidth() + pad, min, max);
                    } else {
                        x2 = x + minWidth;
                    }
                default :
                    break;
            }

            switch (checkY) {
                case LOW : // TOP
                    if (y > (0 - pad)) {
                        min = Math.max(y2 - maxHeight, (0 - pad));
                        max = y2 - minHeight;
                        y = clip((0 - pad), min, max);
                    } else {
                        y = y2 - minHeight;
                    }
                    break;
                case HIGH :// BOTTOM
                    if (y2 < SCREEN_BOUNDS.getHeight() + pad) {
                        min = y + minHeight;
                        max = Math.min(y + maxHeight, SCREEN_BOUNDS.getHeight() + pad);
                        y2 = clip(SCREEN_BOUNDS.getHeight() + pad, min, max);
                    } else {
                        y2 = y + minHeight;
                    }
                default :
                    break;
            }

            updateStagePosition(x, y, x2, y2);
        }
    }

    private double clip(double checkValue, double minValue, double maxValue) {
        if (checkValue < minValue) {
            return minValue;
        }
        if (checkValue > maxValue) {
            return maxValue;
        }
        return checkValue; // unmodified
    }

    private void setXYCheck(CHECK X, CHECK Y) {
        checkX = X;
        checkY = Y;
    }

    /** @return true if checkX and checkY is set to CHECK.NONE */
    private boolean isNone() {
        return checkX.equals(CHECK.NONE) && checkY.equals(CHECK.NONE);
    }

    private void stagePositionInsideScreen() {
        int width = (int) this.stage.getWidth();
        int height = (int) this.stage.getHeight();

        if (stage.getX() + width - pad >= SCREEN_BOUNDS.getWidth()) {
            stage.setX(SCREEN_BOUNDS.getWidth() - width + pad);
        }
        if (stage.getX() + pad < 0.0D) {
            stage.setX(0.0D - pad);
        }
        if (stage.getY() + height - pad >= SCREEN_BOUNDS.getHeight()) {
            stage.setY(SCREEN_BOUNDS.getHeight() - height + pad);
        }
        if (stage.getY() + pad < 0.0D)
            stage.setY(0.0D - pad);
    }

    private void updateStagePosition(double x1, double y1, double x2, double y2) {
        stage.setX(x1);
        stage.setY(y1);
        stage.setWidth(x2 - x1);
        stage.setHeight(y2 - y1);
    }
} // CLASS END

不要忘记根据舞台调整最小和最大尺寸值:

            public void setApplicationContentLayout(AbstractApp app) {
                    Pane contentLayout= app.getRootLayout();
                    BorderPane contentBorderPane = (BorderPane) rootStackPane.getChildren().get(0);
            
                    try {
                        contentBorderPane.setCenter(contentLayout);
// 2 is border width
            contentBorderPane.setMinWidth(contentLayout.getMinWidth() + 2);
            contentBorderPane.setMaxWidth(contentLayout.getMaxWidth() + 2);
// add titlebar height
            contentBorderPane.setMinHeight(contentBorderPane.getMinHeight() + contentLayout.getMinHeight() + 2);
            contentBorderPane.setMaxHeight(contentBorderPane.getMinHeight() + contentLayout.getMaxHeight() + 2);
            
                        stage.setMinWidth(contentBorderPane.getMinWidth() + 4);
                        stage.setMinHeight(contentBorderPane.getMinHeight() + 4);
                        stage.setMaxWidth(contentBorderPane.getMaxWidth() + 4);
                        stage.setMaxHeight(contentBorderPane.getMaxHeight() + 4);
                    } catch (NullPointerException e) {
                        System.out.print("error report:\n");
                        if (contentLayout == null)
                            System.out.print("WindowFrame:setApplicationContent: null \n");
                    }
if (stage.isResizable()) {
            ResizeHelper.install(stage, 28, 7, 0);
        } else {
            System.out.println("ResizeHelper not set, stage not resizable.");
        }
                }

将 CSS 样式添加到边框窗格

.window{
    -fx-effect: dropshadow(three-pass-box, rgb(0,0,0,0.95), 2, 0.6, 0, 1);
    }

答案 12 :(得分:-4)

Undecorater似乎是唯一的解决方案。