当前位置: 首页 > 知识库问答 >
问题:

当窗口/舞台缩小时,为什么容器/部件的位置和比例设置不正确?

丌官积厚
2023-03-14

我正在尝试学习如何使用javafx,但我遇到了一些javafx代码的行为,这些行为对我来说并不真正有意义。我希望您能解释为什么javafx代码会产生这些行为,以及如何绕过它们。

为了说明目的,我编写了一个小的示例程序:

import javafx.application.Application;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.SplitPane;
import javafx.scene.control.ToggleButton;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class Example extends Application {
    @Override
    public void start(Stage primaryStage) {
        // Top section with image
        ImageView imageView = new ImageView(new Image("http://wfarm2.dataknet.com/static/resources/icons/set114/b4f29385.png"));
            imageView.setPreserveRatio(true);
        StackPane stackPane = new StackPane(imageView);
            imageView.fitWidthProperty().bind(stackPane.widthProperty());
            imageView.fitHeightProperty().bind(stackPane.heightProperty());
            stackPane.getStyleClass().add("stack-pane");

        // Bottom Section with CSS toggle
        ToggleButton toggle = new ToggleButton("Show CSS");
        HBox hbox = new HBox(toggle);
            hbox.setAlignment(Pos.CENTER);
            hbox.getStyleClass().add("h-box");

        // Combines the two sections
        SplitPane root = new SplitPane(stackPane, hbox);
            root.setOrientation(Orientation.VERTICAL);
            root.setResizableWithParent(hbox, false);
        Scene scene = new Scene(root);
            toggle.setOnMouseClicked(e -> {
                if (toggle.isSelected()) {
                    scene.getStylesheets().add("StyleSheet.css");
                    toggle.setText("Disable CSS");
                } else {
                    scene.getStylesheets().remove(0);
                    toggle.setText("Show CSS");
                }
            });
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

stylesheet.css文件contains只包含以下三行:

.stack-pane {  -fx-background-color: aqua;  }
.split-pane {  -fx-background-color: crimson;  }
.h-box {  -fx-background-color: chartreuse;  }

    null

我注意到的另外一点是,当窗口/舞台缩小时,图像不会缩小。你知道背后的原因吗?有办法吗?

共有1个答案

贺彬
2023-03-14

当窗口/舞台收缩时,图像不会收缩

是的,那是故意的。Images和ImageViews具有固定的大小。如果您想动态调整ImageViews的大小,那么有以下几个选项:

  • 如何在JavaFX中调整窗口大小时调整图像大小
    null

我并没有用您的代码详细分析这个问题。我认为这可能是由以下几个因素造成的:

  1. 您将ImageView的FitWidth/Height绑定到包含ImageView的StackPane的Width/Height。这可能会导致默认布局代码有点混乱,因为StackPane的宽度/高度的部分决定因素是子级的首选大小,但通过改变FitWidth/height您就改变了ImageView的首选大小,因此很难推断StackPane的宽度和高度应该是什么。
  2. 当没有足够的空间来显示StackPane的内容时,StackPane可以大于可用区域(这在StackPane javadoc中有一点解释)。因此,当您将场景变大时,FitWidth/Height会随着StackPane的可用面积而变大,但当调整大小以变小时,StackPane从未变小,因为它至少需要同样大才能适应之前计算的放大图像视图。

无论如何,这个故事的寓意是:不要将子容器的宽度/高度绑定到父容器的宽度/高度。有时您可以使用这种方法,但有时它会产生奇怪的副作用,如您在示例代码中所观察到的。

相反,您可以重写父节点中的布局传递逻辑,并在那里执行适当的布局计算。下面的示例提供了这种方法的一个示例。不幸的是,Oracle(或网络上几乎任何其他地方)没有关于重写父节点的布局传递逻辑的技术的正式文档。大多数情况下,您不需要编写自定义布局逻辑,而是可以使用标准布局窗格和控件来获得所需的布局(但不幸的是,在这种情况下您不能这样做,必须使用CSS背景设置或自定义布局代码)。

使用-FX-Shape平滑缩放

缩放位图图像会导致某些像素化和图像不是很平滑。在您的例子中,输入位图图像在512x512处非常大,因此平滑缩放对于大图像来说并不是一个问题,例如,如果您只有一个较小的32x32图像,并尝试将其缩放到512x512处,那么平滑缩放就不是一个问题。

由于您有一个简单的黑白图像,您可以将图像转换为svg,从svg中提取路径并使用region属性-FX-shape。这就是默认JavaFX样式表如何定义形状(如复选框的复选框)的方式,使它们能够平滑地跨尺寸缩放。有关-fx-shape的定义和用法的示例,请参阅JRE实现中jfxrt.jar内部的modena.css文件。

类似这样的东西将为您的图像做到这一点(CSS样式可以被提取到一个CSS文件,以便于维护):

Pane tile = new Pane();
tile.setStyle("" +
        "-fx-background-color: forestgreen;" +
        "-fx-shape: \"M2470 4886 c-83 -22 -144 -60 -220 -136 -41 -41 -91 -102 -111 -135\n" +
        "-121 -200 -2027 -3510 -2046 -3554 -55 -125 -68 -272 -33 -377 56 -162 205\n" +
        "-260 431 -284 127 -13 4011 -13 4138 0 226 24 375 122 431 284 35 105 22 252\n" +
        "-33 377 -32 72 -1994 3472 -2064 3577 -135 202 -320 295 -493 248z m223 -1182\n" +
        "c122 -43 210 -155 237 -301 12 -69 3 -156 -45 -428 -34 -196 -101 -636 -120\n" +
        "-785 -8 -69 -18 -144 -21 -168 l-5 -42 -177 2 -177 3 -8 75 c-20 203 -66 500\n" +
        "-158 1030 -38 218 -40 240 -29 305 15 94 47 161 107 221 58 58 126 94 197 104\n" +
        "61 9 147 2 199 -16z m30 -1989 c64 -32 144 -111 175 -172 49 -96 49 -231 0\n" +
        "-332 -29 -59 -102 -133 -165 -164 -275 -140 -595 91 -543 391 14 80 39 130 93\n" +
        "189 86 94 160 124 293 120 76 -2 99 -7 147 -32z\";"
);
tile.setScaleY(-1);
tile.setPrefSize(64, 64);

我不知道在调整窗格大小的情况下,如何使窗格只显示成比例的大小。所以我怀疑这对你有多大帮助。

  1. 要创建布局,请尝试使用Gluon SceneBuilder。即使您不使用FXML,也可以通过动态地使用SceneBuilder中的UI元素和属性,并观察这些更改在生成的FXML文件中产生的差异,轻松地学到很多东西。
  2. 若要在运行时动态调试现有应用程序的布局,请使用FXExperience ScenicView。

样品溶液

不同级尺寸下的采样输出:

import javafx.application.Application;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class ResizableImageSample extends Application {
    @Override
    public void start(Stage stage) {
        // Top section with image
        ImageView imageView = new ImageView(new Image(
                "http://wfarm2.dataknet.com/static/resources/icons/set114/b4f29385.png"
        ));
        imageView.setPreserveRatio(true);
        ImageViewPane imagePane = new ImageViewPane(imageView);
        imagePane.getStyleClass().add("stack-pane");

        // Bottom Section with CSS toggle
        ToggleButton toggle = new ToggleButton("Show CSS");
        HBox hbox = new HBox(toggle);
        hbox.setAlignment(Pos.CENTER);
        hbox.getStyleClass().add("h-box");

        // Combines the two sections
        SplitPane root = new SplitPane(imagePane, hbox);
        root.setDividerPositions(0.8);
        root.setOrientation(Orientation.VERTICAL);
        SplitPane.setResizableWithParent(hbox, false);
        Scene scene = new Scene(root);
        toggle.setOnMouseClicked(e -> {
            if (toggle.isSelected()) {
                scene.getStylesheets().add(getClass().getResource("StyleSheet.css").toExternalForm());
                toggle.setText("Disable CSS");
            } else {
                scene.getStylesheets().remove(0);
                toggle.setText("Show CSS");
            }
        });

        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
/*
 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 */
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Region;

/**
 *
 * @author akouznet
 */
class ImageViewPane extends Region {

    private ObjectProperty<ImageView> imageViewProperty = new SimpleObjectProperty<>();

    public ObjectProperty<ImageView> imageViewProperty() {
        return imageViewProperty;
    }

    public ImageView getImageView() {
        return imageViewProperty.get();
    }

    public void setImageView(ImageView imageView) {
        this.imageViewProperty.set(imageView);
    }

    public ImageViewPane() {
        this(new ImageView());
    }

    @Override
    protected void layoutChildren() {
        ImageView imageView = imageViewProperty.get();
        if (imageView != null) {
            if (imageView.isPreserveRatio()) {
                if (getHeight() > getWidth()) {
                    imageView.setFitWidth(getWidth());
                    imageView.setFitHeight(0);
                } else {
                    imageView.setFitWidth(0);
                    imageView.setFitHeight(getHeight());
                }
            } else {
                imageView.setFitWidth(getWidth());
                imageView.setFitHeight(getHeight());
            }

            layoutInArea(imageView, 0, 0, getWidth(), getHeight(), 0, HPos.CENTER, VPos.CENTER);
        }
        super.layoutChildren();
    }

    public ImageViewPane(ImageView imageView) {
        imageViewProperty.addListener((observable, oldIV, newIV) -> {
            if (oldIV != null) {
                getChildren().remove(oldIV);
            }
            if (newIV != null) {
                getChildren().add(newIV);
            }
        });
        this.imageViewProperty.set(imageView);
    }
}
 类似资料:
  • 但它的行为仍然不正确。 我觉得我做错了,但我不知道如何做对。在这样的窗体上重新缩放控件的正确方法是什么?有没有更好的方法来达到我所期待的结果?

  • 问题内容: 当我尝试使用以下代码将左上角的窗口图标从难看的红色“ TK”更改为我自己的收藏夹图标时,Python抛出错误: 这应该将图标设置为“ favicon.ico”(根据网络上的许多论坛帖子)。但是不幸的是,所有这些行所做的只是抛出以下错误: 我已经做了: 我检查了路径-一切都是100%正确 我尝试了其他文件格式,例如-或均无效 我在许多网站上查询了这个问题 第三点,我最喜欢的Tkinter

  • 本文向大家介绍易语言将当前窗口位置设置成不可移动,包括了易语言将当前窗口位置设置成不可移动的使用技巧和注意事项,需要的朋友参考一下 可否移动属性 数据类型:逻辑型;本属性设置窗口位置能否被移动。 例程 说明 本属性用于设置在按住窗口的标题栏时是否可以移动窗口。 运行结果: 总结 以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对呐喊教程的支持。如果你

  • 问题内容: 所以我已经在Java编程学了一个学期左右的时间,而且我遇到了几次这个问题,最后才开始提出问题。 如果我做一个然后设置大小,例如。帧实际上并不长。据我所知,它实际上更长。另外,如果您将垂直尺寸设置得非常小(低于30),则框架甚至不会显示,只有操作系统顶部的窗口栏和框架才会变大,直到您将值超过30(这样看起来与)相同。为什么会这样,修复起来并不难,但是很奇怪,我很好奇为什么会这样? 如果您

  • 问题内容: 有没有办法在所有浏览器中手动设置浏览器窗口的最小大小? 问题答案: 你可以试试 一旦视口小于600像素,您将获得一个水平滚动条。这仅适用于支持最小宽度CSS属性的现代浏览器。 我认为不可能限制用户的大小调整,也不应该!