javafx - JavaFX 中的 AutoScalePane 与 layoutChildren()

标签 javafx fxml

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

我创建了一个演示应用程序,它使用 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();
    }
}

View Controller :

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 View :

<?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>

自动缩放 Pane :

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

最佳答案

layoutChildren 在节点大小更改时调用。如果您通过 layoutChildren 方法调整比例,则无需注册监听器。

至于缩放:您永远不会真正修改 scale 属性。除了此代码段之外,您不会在任何地方更新 Scale:

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

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

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

顺便说一句:通过扩展 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);
    }

}

关于javafx - JavaFX 中的 AutoScalePane 与 layoutChildren(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49732048/

相关文章:

ScrollPane 中的 JavaFX GridPane 比预期大

java - 将 FXML 文件添加到 ControlsFX 弹出窗口中

Javafx:ListView 禁用列表的指定元素

java - 如何在 Eclipse 中启用 JavaFX 类的自动导入?

带有纯文本的 JavaFX/CSS 透明文本区域

java - 映射后加载 fxml 时,spring 出现 javafx 应用程序异常

java - 如何在 fx :include? 中指定资源

java - 如何在 ListView 中使用自定义单元格下载 fxml?

JavaFX WebView : Start Program, 输入数据、服务并显示本地 HTML -> 刷新数据 = 刷新 WebView

java - MediaPlayer 不播放文件名中的某些字符