我正在努力发展我的编程技能,并正在 JavaFX 中构建 UI 应用程序,因为 Java 是我最擅长的语言。
不幸的是,我发现使用 FXML 构建 UI 充其量是乏味的。我决定将窗口设置为固定大小,认为第一次构建非响应式 UI 会更容易。
添加元素很容易,但事实证明要调整它们的大小是非常困难的,特别是当布局嵌套时(即 HBox 中的 TiledPane)。我认为我误解了孩子如何从父母那里继承属性,反之亦然。
在我的应用程序中,我经常嵌套,因为提供给视图的数据可能需要几秒钟的时间来加载,而且我不想频繁加载屏幕给用户带来负担。
例如,这是屏幕的 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
已经填满了屏幕的其余部分。如果您为其背景着色,您可以看到这一点,例如与 <HBox style='-fx-background-color:skyblue;'>
的文档告诉您HBox
的布局策略如何工作:
会将子项的大小(如果可调整大小)调整为他们喜欢的宽度...。 如果将HBox
的大小调整为大于其首选宽度,则默认情况下它将保持子级的首选宽度,而未使用额外的空间。如果应用程序希望为一个或多个子进程分配额外的空间,则可以选择对子进程设置hbox
约束。有关详细信息,请参阅“可选布局约束”。hgrow
HBox
的两个子节点是另一个HBox
和一个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>
。