JavaFX中的AutoScalePane with layoutChildren()

时间:2018-04-09 11:38:23

标签: javafx fxml

我正在尝试创建自定义窗格,将其内容缩放到窗格的可用空间。

我创建了一个演示应用程序,它将Stage与SplitPane分开。每个拆分包含一个AutoScalePane(请参阅FMXL)。我希望AutoScalePane根据可用空间缩小/增加其内容(请使用分割栏进行播放)

当AutoScalePane边界发生变化时,AutoScalePane的内容将分组到一个组中,该组应进行缩放。

即使我收到正确的边界并且可以计算正确的缩放比例(检查调试日志),圆形节点也不会缩放..

我认为我在layoutChildren()方法中犯了一个错误,但我看不出明显的问题。

如果有更多JavaFX经验的人可以帮助我,那将会很棒。)

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("AutoScalePane Test");
        primaryStage.setScene(new Scene(root, 700, 200));
        primaryStage.show();
    }
}

查看控制器:

public class Controller {
    @FXML
    public AutoScalePane scalePaneLeft;

    @FXML
    public AutoScalePane scalePaneRight;

    @FXML
    public void initialize() {
        fillLeftContent();
        fillRightContent();
    }

    private void fillLeftContent() {
        Circle circle1 = new Circle(100, 300, 10);
        Circle circle2 = new Circle(150, 300, 10);
        Circle circle3 = new Circle(200, 300, 10);
        Circle circle4 = new Circle(250, 300, 10);

        scalePaneLeft.addChildren(new Node[] {circle1, circle2, circle3,
                circle4});
    }

    private void fillRightContent() {
        Circle circle1 = new Circle(100, 200, 20);
        Circle circle2 = new Circle(150, 200, 20);
        Circle circle3 = new Circle(200, 200, 20);
        Circle circle4 = new Circle(250, 200, 20);

        scalePaneRight.addChildren(new Node[] {circle1, circle2, circle3,
                circle4});
    }
}

FXML查看:

<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import sample.AutoScalePane?>
<AnchorPane fx:controller="sample.Controller"
            xmlns:fx="http://javafx.com/fxml">


    <SplitPane dividerPositions="0.3" orientation="HORIZONTAL" AnchorPane.topAnchor="0" AnchorPane.bottomAnchor="0"
               AnchorPane.leftAnchor="0" AnchorPane.rightAnchor="0" style="-fx-background-color: #2c5069;">

        <AutoScalePane fx:id="scalePaneLeft"
                       style="-fx-background-color: #943736;"/>

        <AutoScalePane fx:id="scalePaneRight"
                       style="-fx-background-color: #d27452;"/>

    </SplitPane>

</AnchorPane>

自动缩放窗格:

   /**
     * Auto-scales its content according to the available space of the Pane.
     * The content is always centered
     *
     */
    public class AutoScalePane extends Pane {

        private Group content = new Group();
        private Scale zoom = new Scale(1, 1);

        public AutoScalePane() {
            layoutBoundsProperty().addListener((o) -> {
                autoScale();
            });

            content.scaleXProperty().bind(zoom.xProperty());
            content.scaleYProperty().bind(zoom.yProperty());

            getChildren().add(content);
        }

        /**
         * Adds nodes to the AutoScalePane
         *
         * @param children nodes
         */
        public void addChildren(Node... children) {
            content.getChildren().addAll(children);
            requestLayout();
        }

        private void autoScale() {
            if (getHeight() > 0
                    && getWidth() > 0
                    && content.getBoundsInParent().getWidth() > 0
                    && content.getBoundsInParent().getHeight() > 0) {

                // scale
                double scaleX = getWidth() / content.getBoundsInParent().getWidth();
                double scaleY = getHeight() / content.getBoundsInParent()
                        .getHeight();

                System.out.println("*************** DEBUG ****************");
                System.out.println("Pane Width: " + getWidth());
                System.out.println("Content Bounds Width: " + content
                        .getBoundsInParent()
                        .getWidth());
                System.out.println("Pane Height: " + getHeight());
                System.out.println("Content Bounds Height: " + content
                        .getBoundsInParent()
                        .getHeight());
                System.out.println("ScaleX: " + scaleX);
                System.out.println("ScaleY: " + scaleY);

                double zoomFactor = Math.min(scaleX, scaleY);
                zoom.setX(zoomFactor);
                zoom.setY(zoomFactor);

                requestLayout();
            }
        }

        @Override
        protected void layoutChildren() {
            final double paneWidth = getWidth();
            final double paneHeight = getHeight();
            final double insetTop = getInsets().getTop();
            final double insetRight = getInsets().getRight();
            final double insetLeft = getInsets().getLeft();
            final double insertBottom = getInsets().getBottom();

            final double contentWidth = (paneWidth - insetLeft - insetRight) *
                    zoom.getX();
            final double contentHeight = (paneHeight - insetTop - insertBottom) *
                    zoom.getY();

            layoutInArea(content, 0, 0, contentWidth, contentHeight,
                    getBaselineOffset(), HPos.CENTER, VPos.CENTER);
        }
    }

1 个答案:

答案 0 :(得分:1)

在更改节点大小时调用

layoutChildren。如果从layoutChildren方法调整比例,则无需注册监听器。

至于缩放:你永远不会真正修改scale属性。您不会在此代码段中的任何位置更新Scale

double zoomFactor = Math.min(zoom.getX(), zoom.getY());
zoom.setX(zoomFactor);
zoom.setY(zoomFactor);

所以zoom.getX()zoom.getY()始终返回1,它等于初始比例因子。

请注意,您可以直接将Scale矩阵应用于内容节点的transforms,但这不会将中心用作缩放的轴心点。

BTW:通过扩展Region代替Pane,您可以将对children列表的访问权限限制为protected,从而阻止用户修改它。

public class AutoScalePane extends Region {

    private final Group content = new Group();

    public AutoScalePane() {
        content.setManaged(false); // avoid constraining the size by content
        getChildren().add(content);
    }

    /**
     * Adds nodes to the AutoScalePane
     *
     * @param children nodes
     */
    public void addChildren(Node... children) {
        content.getChildren().addAll(children);
        requestLayout();
    }

    @Override
    protected void layoutChildren() {
        final Bounds groupBounds = content.getBoundsInLocal();

        final double paneWidth = getWidth();
        final double paneHeight = getHeight();
        final double insetTop = getInsets().getTop();
        final double insetRight = getInsets().getRight();
        final double insetLeft = getInsets().getLeft();
        final double insertBottom = getInsets().getBottom();

        final double contentWidth = (paneWidth - insetLeft - insetRight);
        final double contentHeight = (paneHeight - insetTop - insertBottom);

        // zoom
        double factorX = contentWidth / groupBounds.getWidth();
        double factorY = contentHeight / groupBounds.getHeight();
        double factor = Math.min(factorX, factorY);
        content.setScaleX(factor);
        content.setScaleY(factor);

        layoutInArea(content, insetLeft, insetTop, contentWidth, contentHeight,
                getBaselineOffset(), HPos.CENTER, VPos.CENTER);
    }

}