我正在努力培养我的编程技能,并使用 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 的一种糟糕方式,我需要重构。
解决当前问题
首先请注意,
HBox
已经填满了屏幕的其余部分。如果你给背景上色,你就会发现这一点,例如<HBox style='-fx-background-color:skyblue;'>
的文档
HBox
告诉您布局策略是如何工作的HBox
:的两个子节点
HBox
是 anotherHBox
和 aStackPane
。当空间超过这些节点所需的空间时,HBox
会将它们调整为它们所需的大小,并将剩余空间留空。您可以通过设置hgrow
约束为子节点分配额外空间,但您需要指定哪些节点获得额外空间。例如,要将其仅分配给StackPane
,您可以HBox.hgrow="ALWAYS"
在 的声明中添加StackPane
:您可以使用以下方法实现相同目的
BorderPane
:这是因为布局策略为
BorderPane
中心分配了额外的空间。再次参考文档:在这种情况下,顶部、底部和右侧均为空,因此它们均占用零空间。
<HBox>
左侧的 占用其首选宽度,其余空间分配给<StackPane>
。布局系统如何工作
包文档
javafx.scene.layout
描述了一般的布局机制。在用 JavaFX 编写的独立桌面应用程序中,一个窗口只包含一个
Scene
。 的大小Scene
由窗口决定;基本上填充了窗口中除“装饰”(标题栏、窗口按钮、边框)之外的所有空间。有
Scene
且仅有一个根节点,类型为Parent
,并且根的大小可以填充整个场景,无论其具体Parent
子类型如何。布局场景时,会为根节点分配其大小(即场景的大小),然后
layout()
调用其方法,该方法又会调用其layoutChildren()
方法。布局以“自上而下”的方式进行。这意味着根节点将查询其子节点的首选、最小和最大大小(如果它们可调整大小),并根据布局策略、可用空间、子节点的首选大小以及任何设置(例如上面使用的设置)为每个子节点分配大小和位置hgrow
。然后对于每个也是
Parent
实例的子级,layout()
(以及因此layoutChildren()
)依次被调用。所有这些的结果是,根节点被分配一个大小,它根据该大小定位和调整每个直接子节点的大小。然后,每个子节点在分配大小后执行其布局。
这实际上非常适合您描述的“嵌套”设计,并且实际上是为这种设置和响应式 UI 而设计的。
成功使用的关键是了解所有预定义的布局窗格以及它们的大致工作原理。所有这些都在
javafx.scene.layout
包中(其文档链接在上面)。还有一个教程。通常,布局窗格会尝试将其子节点调整为其首选大小,并且大多数布局窗格会尽可能遵守最小和最大尺寸。每个布局窗格的文档都描述了如何计算其最小/首选/最大尺寸以及如何使用其子节点的尺寸来布局它们。
针对您的问题和代码的具体评论
如果您不想,则无需在 JavaFX 应用程序中使用 FXML。如果您愿意,您可以完全用 Java 创建 UI。使用 FXML 有优点也有缺点:
优点:
缺点:
这可能并非事实;如上所述,布局机制是专门为响应式 UI 设计的,因此通过使用固定的窗口大小,您可以有效地利用 API。
避免任何硬编码大小,例如
maxWidth="666"
。偶尔,您可能会有一个元素,其最大大小设置为其计算(首选)大小,并且您可以设置maxWidth="Infinity"
为允许它增长,如果这是所需的效果。此处显示了一个示例。避免使用不必要的窗格。您的窗格
StackPane
包含三个StackPane
,每个窗格仅包含一个子节点。这使得三个子StackPane
节点变得多余。考虑替换此窗格:和