AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 79276404
Accepted
TheOrnithologist
TheOrnithologist
Asked: 2024-12-13 03:47:21 +0800 CST2024-12-13 03:47:21 +0800 CST 2024-12-13 03:47:21 +0800 CST

如何一致地规定嵌套 JavaFX 控件的大小?

  • 772

我正在努力培养我的编程技能,并使用 JavaFX 构建 UI 应用程序,因为 Java 是我最擅长的语言。

不幸的是,我发现使用 FXML 构建 UI 非常繁琐。我决定将窗口设置为固定大小,因为我认为第一次构建无响应 UI 会更容易。

添加元素很容易,但要让它们调整到合适的大小却非常困难,尤其是当布局嵌套时(例如,TiledPane 位于 HBox 中)。我想我误解了子元素如何从父元素继承属性,反之亦然。

在我的应用程序中,我经常嵌套,因为提供给视图的数据可能需要几秒钟才能加载,并且我不想给用户带来频繁的加载屏幕的负担。

例如,这是根据您按下的按钮显示数据的屏幕的 FXML。我的窗口是 1600x900。我希望第二个 HBox 中的元素填满屏幕的其余部分,但我似乎无法调整它们。我还想调整其他元素的大小,但我认为一旦我理解了我所缺少的任何原则,这将变得更容易。

<VBox fx:id="detachmentView" visible="false">
                <HBox alignment="CENTER">
                    <Button fx:id="detachmentRules" text="Rules" styleClass="medium_button" onAction="#detachmentRulesButton"/>
                    <Button fx:id="detachmentStratagems" text="Stratagems" styleClass="medium_button" onAction="#detachmentStratagemButton"/>
                    <Button fx:id="detachmentEnhancements" text="Enhancements" styleClass="medium_button" onAction="#detachmentEnhancementButton"/>
                </HBox>
                <Separator/>
                <HBox>
                    <HBox maxWidth="666">
                        <VBox>
                            <Label text="Detachment Name:" styleClass="detachment_header"/>
                            <ListView fx:id="detachmentList" onMouseClicked="#detachmentListSelect" styleClass="detachment_list" prefWidth="333"/>
                        </VBox>
                        <VBox fx:id="stratagemHeader" visible="false" managed="false">
                            <Label text="Stratagem Name:"  styleClass="detachment_header"/>
                            <ListView fx:id="stratagemList" onMouseClicked="#stratagemListSelect" styleClass="detachment_list" prefWidth="333"/>
                        </VBox>
                        <VBox fx:id="enhancementHeader" visible="false" managed="false">
                            <Label text="Enhancement Name:" styleClass="detachment_header"/>
                            <ListView fx:id="enhancementList" onMouseClicked="#enhancementListSelect" styleClass="detachment_list" prefWidth="333"/>
                        </VBox>
                    </HBox>
                    <StackPane>
                        <StackPane fx:id="detachmentRulesView">
                            <VBox>
                                <Label fx:id="detachmentLabel" styleClass="detachment_header_big"/>
                                <VBox fx:id="dynamicVBox"/>
                            </VBox>
                        </StackPane>
                        <StackPane fx:id="detachmentStratagemView" visible="false">
                            <BorderPane>
                                <center>
                                    <VBox>
                                        <Label fx:id="stratagemID" styleClass="detachment_header"/>
                                        <Label fx:id="stratagemType" styleClass="detachment_header"/>
                                        <Label fx:id="stratagemCost" styleClass="detachment_header"/>
                                        <Label fx:id="stratagemPhase" styleClass="detachment_header"/>
                                        <WebView fx:id="stratagemRules" style="-fx-pref-height: 250;"/>
                                        <TextArea fx:id="stratagemLegend" wrapText="true" editable="false"/>
                                    </VBox>
                                </center>
                            </BorderPane>
                        </StackPane>
                        <StackPane fx:id="enhancementView" visible="false">
                            <BorderPane>
                                <center>
                                    <VBox>
                                        <Label fx:id="enhancementName" styleClass="detachment_header"/>
                                        <Label fx:id="enhancementCost" styleClass="detachment_header"/>
                                        <WebView fx:id="enhancementRules" style="-fx-pref-height: 250;"/>
                                        <TextArea fx:id="enhancementLegend" wrapText="true" editable="false"/>
                                    </VBox>
                                </center>
                            </BorderPane>
                        </StackPane>
                    </StackPane>
                </HBox>
            </VBox>

它产生如下结果: FXML 元素大小调整

我已阅读 JavaFX 文档、多个专门介绍 JavaFX 的网站、本论坛和 ChatGPT。我尝试在许多不同的父元素和子元素上为相关元素添加各种修饰符,如 prefHeight、maxHeight、minHeight、VBox.vgrow,但均无济于事。我在其他地方使用过这些修饰符,但效果不错,因此我不确定我做错了什么。

这也完全可能是构建 UI 的一种糟糕方式,我需要重构。

javafx
  • 1 1 个回答
  • 26 Views

1 个回答

  • Voted
  1. Best Answer
    James_D
    2024-12-13T04:11:24+08:002024-12-13T04:11:24+08:00

    解决当前问题

    我希望第二个 HBox 中的元素填满屏幕的其余部分

    首先请注意,HBox已经填满了屏幕的其余部分。如果你给背景上色,你就会发现这一点,例如<HBox style='-fx-background-color:skyblue;'>

    的文档HBox告诉您布局策略是如何工作的HBox:

    HBox将调整子元素(如果可调整大小)的大小以使其适合其首选宽度...。如果hbox调整后的大小大于其首选宽度,则默认情况下,它将保持子元素的首选宽度,而将多余的空间保留下来。如果应用程序希望为一个或多个子元素分配额外的空间,则可以选择hgrow为子元素设置约束。有关详细信息,请参阅“可选布局约束”。

    的两个子节点HBox是 anotherHBox和 a StackPane。当空间超过这些节点所需的空间时,HBox会将它们调整为它们所需的大小,并将剩余空间留空。您可以通过设置hgrow约束为子节点分配额外空间,但您需要指定哪些节点获得额外空间。例如,要将其仅分配给StackPane,您可以HBox.hgrow="ALWAYS"在 的声明中添加StackPane:

    <VBox fx:id="detachmentView">
        <HBox alignment="CENTER">
            <!-- ... -->
        </HBox>
        <Separator />
        <HBox>
            <HBox maxWidth="666">
                <!-- ... -->
            </HBox>
            <StackPane HBox.hgrow="ALWAYS">
                <!-- ... -->
            </StackPane>
        </HBox>
    </VBox>
    

    您可以使用以下方法实现相同目的BorderPane:

    <VBox fx:id="detachmentView">
        <HBox alignment="CENTER">
            <!-- ... -->
        </HBox>
        <Separator />
        <BorderPane>
            <left>
                <HBox maxWidth="666">
                    <!-- ... -->
                </HBox>
            </left>
            <center>
                <StackPane>
                    <!-- ... -->
                </StackPane>
            </center>
        </BorderPane>
    </VBox>
    

    这是因为布局策略为BorderPane中心分配了额外的空间。再次参考文档:

    顶部和底部子节点将调整为它们首选的高度并扩展边框窗格的宽度。左侧和右侧子节点将调整为它们首选的宽度并扩展顶部和底部节点之间的长度。中心节点将调整大小以填充中间的可用空间。任何位置都可以为空。

    在这种情况下,顶部、底部和右侧均为空,因此它们均占用零空间。<HBox>左侧的 占用其首选宽度,其余空间分配给<StackPane>。

    布局系统如何工作

    包文档javafx.scene.layout描述了一般的布局机制。

    一旦应用程序创建并显示场景,系统就会自动驱动场景图布局机制。场景图会检测影响布局的动态节点更改(例如大小或内容的更改)并调用requestLayout(),这会将该分支标记为需要布局,以便在下一个脉冲上,通过调用该分支的根来对该分支执行自上而下的布局过程layout()。在该布局过程中,layoutChildren()将在每个父级上调用回调方法来布局其子级。

    在用 JavaFX 编写的独立桌面应用程序中,一个窗口只包含一个Scene。 的大小Scene由窗口决定;基本上填充了窗口中除“装饰”(标题栏、窗口按钮、边框)之外的所有空间。

    有Scene且仅有一个根节点,类型为Parent,并且根的大小可以填充整个场景,无论其具体Parent子类型如何。

    布局场景时,会为根节点分配其大小(即场景的大小),然后layout()调用其方法,该方法又会调用其layoutChildren()方法。布局以“自上而下”的方式进行。这意味着根节点将查询其子节点的首选、最小和最大大小(如果它们可调整大小),并根据布局策略、可用空间、子节点的首选大小以及任何设置(例如上面使用的设置)为每个子节点分配大小和位置hgrow。

    然后对于每个也是Parent实例的子级,layout()(以及因此layoutChildren())依次被调用。

    所有这些的结果是,根节点被分配一个大小,它根据该大小定位和调整每个直接子节点的大小。然后,每个子节点在分配大小后执行其布局。

    这实际上非常适合您描述的“嵌套”设计,并且实际上是为这种设置和响应式 UI 而设计的。

    成功使用的关键是了解所有预定义的布局窗格以及它们的大致工作原理。所有这些都在javafx.scene.layout包中(其文档链接在上面)。还有一个教程。

    通常,布局窗格会尝试将其子节点调整为其首选大小,并且大多数布局窗格会尽可能遵守最小和最大尺寸。每个布局窗格的文档都描述了如何计算其最小/首选/最大尺寸以及如何使用其子节点的尺寸来布局它们。

    针对您的问题和代码的具体评论

    不幸的是,我发现使用 FXML 构建 UI 非常繁琐。

    如果您不想,则无需在 JavaFX 应用程序中使用 FXML。如果您愿意,您可以完全用 Java 创建 UI。使用 FXML 有优点也有缺点:

    优点:

    • FXML 是分层的,比 Java(实际上是一种命令式语言)更自然地表示场景图的层次结构。因此,在 FXML 文档中放置元素实际上只有一个顺序,而 Java 中有许多不同的操作顺序,但会产生相同的场景图。
    • 综上所述,创建利用 FXML 的 UI 设计工具(例如SceneBuilder)比创建生成 Java 代码的工具要容易得多。在 FXML 中,只有一种构造代码的方法,因此它将生成程序员很有可能理解的代码,而且由于给定场景图只有一种结构,因此手写的 FXML 比手写的 Java 更容易被工具解析。
    • 至少在理论上,使用 FXML 可以促进团队中的分工,UI 设计师可以使用 FXML 而无需了解 Java

    缺点:

    • 使用 FXML 并不能免除程序员理解布局窗格、其属性以及它们如何工作的负担,即使使用 SceneBuilder 等 UI 工具也是如此
    • FXML 未经编译,因此没有编译时类型检查或其他形式的有效性检查(尽管某些 IDE 可以进行一些基本检查)
    • FXML 很冗长
    • 如果你已经熟悉 Java,那么使用 FXML 需要学习一种新的(尽管非常简单的)语言

    我决定将窗口设置为固定大小,认为第一次构建无响应的 UI 会更容易。

    这可能并非事实;如上所述,布局机制是专门为响应式 UI 设计的,因此通过使用固定的窗口大小,您可以有效地利用 API。

    避免任何硬编码大小,例如maxWidth="666"。偶尔,您可能会有一个元素,其最大大小设置为其计算(首选)大小,并且您可以设置maxWidth="Infinity"为允许它增长,如果这是所需的效果。此处显示了一个示例。

    避免使用不必要的窗格。您的窗格StackPane包含三个StackPane,每个窗格仅包含一个子节点。这使得三个子StackPane节点变得多余。考虑替换此窗格:

    <VBox fx:id="detachmentView">
        <HBox alignment="CENTER">
            <!-- ... -->
        </HBox>
        <Separator />
        <HBox>
            <HBox maxWidth="666">
                <!-- ... -->
            </HBox>
            <StackPane HBox.hgrow="ALWAYS">
                <StackPane fx:id="detachmentRulesView">
                    <VBox>
                        <!-- -->
                    </VBox>
                </StackPane>
                <StackPane fx:id="detachmentStratagemView" >
                    <BorderPane>
                        <!-- -->
                    </BorderPane>
                </StackPane>
                <StackPane fx:id="enhancementView" >
                    <BorderPane>
                        <!-- -->
                    </BorderPane>
                </StackPane>
            </StackPane>
        </HBox>
    </VBox>
    

    和

    <VBox fx:id="detachmentView">
        <HBox alignment="CENTER">
            <!-- ... -->
        </HBox>
        <Separator />
        <HBox>
            <HBox maxWidth="666">
                <!-- ... -->
            </HBox>
            <StackPane HBox.hgrow="ALWAYS">
                <VBox fx:id="detachmentRulesView">
                    <!-- -->
                </VBox>
                <BorderPane fx:id="detachmentStratagemView">
                    <!-- -->
                </BorderPane>
                <BorderPane fx:id="enhancementView">
                    <!-- -->
                </BorderPane>
            </StackPane>
        </HBox>
    </VBox>
    
    • 2

相关问题

  • 如何从字节数组在 javafx 中创建 Media 对象?

  • Node.snapshot() 在 JavaFX 中看不到节点的变化

  • JavaFX 场景 minWidth 和 minHeight 不起作用

  • 如何使 Togglebutton 在 tableView 列中正常工作?

  • Javafx ListView 遇到问题

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    Vue 3:创建时出错“预期标识符但发现‘导入’”[重复]

    • 1 个回答
  • Marko Smith

    为什么这个简单而小的 Java 代码在所有 Graal JVM 上的运行速度都快 30 倍,但在任何 Oracle JVM 上却不行?

    • 1 个回答
  • Marko Smith

    具有指定基础类型但没有枚举器的“枚举类”的用途是什么?

    • 1 个回答
  • Marko Smith

    如何修复未手动导入的模块的 MODULE_NOT_FOUND 错误?

    • 6 个回答
  • Marko Smith

    `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它?

    • 3 个回答
  • Marko Smith

    何时应使用 std::inplace_vector 而不是 std::vector?

    • 3 个回答
  • Marko Smith

    在 C++ 中,一个不执行任何操作的空程序需要 204KB 的堆,但在 C 中则不需要

    • 1 个回答
  • Marko Smith

    PowerBI 目前与 BigQuery 不兼容:Simba 驱动程序与 Windows 更新有关

    • 2 个回答
  • Marko Smith

    AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String”

    • 1 个回答
  • Marko Smith

    我正在尝试仅使用海龟随机和数学模块来制作吃豆人游戏

    • 1 个回答
  • Martin Hope
    Aleksandr Dubinsky 为什么 InetAddress 上的 switch 模式匹配会失败,并出现“未涵盖所有可能的输入值”? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge 为什么这个简单而小的 Java 代码在所有 Graal JVM 上的运行速度都快 30 倍,但在任何 Oracle JVM 上却不行? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini 具有指定基础类型但没有枚举器的“枚举类”的用途是什么? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer 何时应使用 std::inplace_vector 而不是 std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller 在 5.2 版中,bash 条件语句中的 [[ .. ]] 中的分号现在是可选的吗? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench 为什么双破折号 (--) 会导致此 MariaDB 子句评估为 true? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng 为什么 `dict(id=1, **{'id': 2})` 有时会引发 `KeyError: 'id'` 而不是 TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String” 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB 为什么 GCC 生成有条件执行 SIMD 实现的代码? 2024-02-17 06:17:14 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve