JavaFX Layouts
JavaFX 中的布局容器提供了各种内置功能,用于设置组件节点的位置、大小、对齐。
一共提供了 9 种默认的布局容器,包括基类 Pane
,该类仅用于设置位置。
- Pane
- AnchorPane
- BorderPane
- GridPane
- StackPane
- TilePane
- HBox
- VBox
另外一些布局容器用于支持用户控制查看节点的视窗,而非布局:
- ScrollPane:支持滚动或平移窗格。
- TabPane:通过选择 Tab 来查看不同的窗格。
- SplitPane:通过移动分隔符来改变两个可见窗格的比例。
布局的选择取决于两个关键因素:
- 你想要你的节点所处的位置(布局类型)
- 布局的方式将控制其子元素的位置、大小和对齐方式
选择布局之后,基于布局功能设置节点的位置、大小、对齐。
布局类型
可以将布局分为 3 个大类:块(block)、网格(grid)、行列(row/column)。
Block 布局
块布局是最简单的,只有一组规则来定义其子元素的布局。我称它们为“块”布局,因为它们不会将节点划分为网格(网格布局)或行列(行/列布局)。
也就是说,从布局逻辑的角度来看,窗格是单个块。
- StackPane:节点位置的设置由 对齐 设置来控制。
- AnchorPane:阶段位置的设置由 ”绝对位置“ 设置来控制。
- 绝对位置表示可以同时控制位置和大小,StackPane 则仅能控制位置。
Grid 布局
网格布局是最复杂的,可以非常详细地控制节点的位置。
- TilePane:最简单,但也提供了最少的控制能力。
- BorderPane:支持对定位的粗略控制,但在默认情况下也保证了确定行为。这对于默认窗口行为用例来说是一个很好的平衡。
- GridPane:支持非常精细的控制节点位置,但使用起来可能很困难和复杂。
Row/Column 布局
以顺序方式排列节点,横向或纵向。
其中每种布局都可以控制序列中节点之间的间距。对于 FlowPane,用户还可以在多行和多列设置中控制行或列之间的空间。
选择布局
基于控制子元素的方式来选择需要的布局。
窗格用于以各种方式控制子元素的位置、大小和对齐。这将影响面板调整大小时它如何移动和调整节点的大小。
值得记住的是,窗口经常被用户调整大小,比你默认设定的的要小或大。
以下是各种布局如何控制子节点:
Pane | Layout Positions Children | Resizes Children | Aligns Children | Has sub-regions | Children can overlap |
---|---|---|---|---|---|
AnchorPane | ✔ | ✔ | ✔ | ||
StackPane | ✔ | ✔ | |||
BorderPane | ✔ | ✔ | ✔ | ✔ | |
GridPane | ✔ | ✔ | (virtual) | ||
TilePane | ✔ | ✔ | (virtual) | ||
FlowPane | ✔ | ✔ | |||
HBox | ✔ | ✔ | ✔ | ||
VBox | ✔ | ✔ | ✔ |
使用布局的一个常见问题是试图调整控件的大小。如果你在调整控件的布局时遇到了麻烦,比如按钮,可以查看下拉菜单来修复它。
深入布局
Pane
类是所有布局的基类,不会对其子布局应用任何布局行为。因此,当您需要绝对定位一个节点,但又不希望有任何额外的调整大小行为或对齐时,Pane 就够用了。
每个布局窗格扩展了 Pane 类,并在窗格中定义的核心行为之上提供了附加的功能层。布局窗格没有任何魔法:它们通过根据一组特定于它们试图创建的布局的规则手动计算每个子节点的 layoutX 和 layoutty 属性来实现它们想要的布局行为。
AnchorPane
- 区域是一个单独的块,任何节点都可以在上面定位
- 在节点上设置的锚点将被应用,覆盖任何现有的最大或最小属性值
- 重叠节点从 observableelist 子节点的开始呈现到结束,这意味着在默认情况下,后添加的项最后呈现
应用方法
除了使用 layoutX 和 layoutY 进行绝对定位之外,AnchorPane 还有四个方法来定义它的子组件的布局。
setTopAnchor(Double double)
setBottomAnchor(Double value)
setRightAnchor(Double value)
setLeftAnchor(Double value)
这些方法定义了子元素的边缘相对于 AnchorPane 的指定边缘的绝对位置。
这些方法可以同时用于设置位置和大小。
要更改重叠节点的呈现顺序,请使用 getChildren()
获取子节点列表,并更改顺序。
设置位置
在一个节点上设置一个或两个锚点将会影响到根据 AnchorPane 的布局边界来定位该节点。
将锚点设置为 0 将把节点的指定一侧粘到 AnchorPane 的同一侧。
注意:锚设置方法接受双精度值,可能为负值,但可能没有您想象的效果。
如果我尝试设置一个负的左锚,我可能期望节点看起来像是从 AnchorPane 的左边缘滑出。相反,AnchorPane 尝试调整自己的大小以适应节点。
如果是为上部的节点设置一个负锚点,看起来仍然像粘在面板的左边。而其他的节点看起来只是向右移动了。
如果想要成功实现从一个 AnchorPane 的左边滑出的预期效果,你需要将滑动和剪切结合起来(sliding with clipping)。
设置大小
在一个节点上设置三个锚,将会有拉伸两个相反的锚之间的节点的效果。
这可能非常有用,因为它会覆盖节点上的任何最大或最小宽度或高度。通过使用所有四个锚点,可以在两个方向上拉伸节点。
StackPane
StackPane 是一个简单的布局窗格,类似于 AnchorPane。这两个窗格都是相对简单的布局,支持呈现相互重叠的节点。但是,AnchorPane 专注于绝对定位,而 StackPane 专注于基于对齐的节点定位。
应用方法
为了充分利用 StackPane,我通常先对对齐进行排序,然后再进行定位,所以在本例中,我将先进行对齐,然后再进行定位。
StackPane 也不会调整节点的大小。与锚窗格不同的是,锚窗格可以通过在子节点边界上强制绝对定位来拉伸节点,而 StackPane 专注于对齐,这意味着它不会调整任何节点的大小。
设置对齐
默认情况下,所有节点都在 StackPane 的中心对齐。
要更改节点的默认对齐方式,需要在相关的 StackPane 上使用 setAlignment(Pos value)
。
为了使节点准确地位于您想要的位置,您可能需要使用 translateX 和 translateY 属性来修改对象的位置。事实上,当我开始使用 StackPanes 时,我经常将侦听器挂接到可以调整节点翻译属性的 StackPane 宽度上。
停! 用边距(margin)代替。
要调整节点位置,可以使用静态方法 StackPane.setMargin(Node child, Insets insets)
,引用要对齐的节点和需要的对齐方式。
设置位置
StackPane 不支持节点的绝对定位,任何 layoutX 和 layoutY 的值都会被 StackPane 忽略。通过逐个节点设置边距,或更改节点的 translateX 和 translateY 值,可以对节点的位置进行微调。
Margins:边距
我认为使用 StackPane 最安全的方法是使用边距,而不是手动修改对象的 translate 属性。在本例中,您可以通过调用静态方法 StackPane.setMargin(Node node, Insets margin)
,在每个节点周围手动创建一个透明的边距。
手动修改
您可以使用 translateX 和 translateY 属性来调整子节点的位置。这对于添加或删除节点的动画或过渡特别有用。
这些可能非常有用,但请记住,它们是在计算完 StackPane 的布局边界之后应用的,所以带有修改的 translate 属性的节点可能会在 StackPane 的边界之外呈现。
注意:当一个 translate 属性被修改时,它将在所有对齐和插入之后被应用,所以在计算修改这些属性的数量时,请记住要考虑这些因素。
BorderPane
BorderPane 被设计为具有固定高度的顶部和底部区域,以及固定宽度的左右区域。其效果是,随着 BorderPane 改变尺寸,中心的大小也会变化。
应用方式
其应用方式包含三个部分:位置、大小、对齐。
设置位置
与我们迄今为止看到的窗格不同,这些窗格使用 getChildren()
方法访问节点的子节点,这与 BorderPane 不兼容。
由于设计意图以特定的方式托管子节点,我们需要告诉 BorderPane 要将节点添加到哪个区域。在 BorderPane 中设置节点的五种方法是:
- Centre:
setCenter(Node node)
- Left:
setLeft(Node node)
- Right:
setRight(Node node)
- Top:
setTop(Node node)
- Bottom:
setBottom(Node node)
在 BorderPane 的每个区域中只能存储一个节点,因此多次调用这些方法将删除原始节点,并用最近的赋值替换它。
设置大小
BorderPane 的大小调整行为是它特别适合于标准窗口布局的原因之一。它的设计是为了适应固定高度但可变宽度的菜单栏和固定宽度但可变高度的导航窗口。
由于这种确切的设计意图,BorderPane 对节点大小的影响取决于节点在 BorderPane 中的位置。以下是关于 BorderPane 如何调整其子节点大小的细节:
Top、Bottom
- Height:顶部和底部窗格中的节点固定在它们的首选高度。
- Width:顶部和底部区域节点的宽度根据 BorderPane 的宽度在其可调整大小的范围内进行调整。
- 如果要求 BorderPane 缩小到顶部/底部节点的最小宽度以下,它们仍然忠实地以最小大小呈现。
Left、Right
- Height:左右区域节点的高度根据 BorderPane 的高度在其可调整大小的范围内进行调整。计算结果为 BorderPane 的高度减去顶部和底部区域的高度。
- 如果要求 BorderPane 缩小到左/右节点的最小高度以下,它们仍然忠实地以最小大小呈现。
- Width:左右窗格中的节点固定在它们的首选宽度上。
Center
- Height:中心区域的宽度计算为 BorderPane 的宽度减去左右节点的首选宽度之和。
- Width:中心区域的高度计算为边框窗格的高度减去顶部和底部节点的首选高度之和。
如果要求 BorderPane 收缩到中心节点的最小高度或宽度以下,它仍将忠实地呈现在其最小大小。
设置对齐
在以下情况下,BorderPane 将在其区域的节点周围创建空间:
- BoderPane 的宽度大于顶部或底部节点的最大宽度。
- BorderPane 的高度大于左右节点的最大高度。
- 中间剩余空间大于中心节点的最大尺寸。
如果任何 BorderPane 区域的大小大于其包含节点的最大大小,默认情况下它将按照以下规则进行定位:
- Top/Bottom/Left/Right:Top-Left 对齐。(记住,对于 Top 和 Bottom,区域的大小为 preheight,所以垂直对齐不会被注意到,左/右和水平对齐也是如此。
- Center:中心对齐。
可以使用静态方法 BorderPane.setAlignment(Node node, Pos alignment)
设置 BorderPane 区域内任何节点的对齐方式。或者 FXML 文件中使用 BorderPane.alignment="<alignment>"
。
GridPane
GridPane 布局为网格布局中的节点提供了最好的控制级别。这包括为对齐和调整大小设置特定于列和行属性的功能,以及特定于节点的跨行和跨列行为。
应用方式
GridPane 是 JavaFX 最复杂的默认布局,有多层的位置和大小控制。
几乎所有这些都涉及到创建 RowConstraints 或 ColumnConstraints 对象。涉及的方法为:getColumnConstraints().add(ColumnConstraints constraint)
或 getRowConstraints().add(RowConstraints constraint)
。
约束按添加到 GridPane 的顺序设置到列和行。向 GridPane 中添加空约束(如 new ColumnConstraints()
)将产生不设置任何约束的效果。如果您想要跳过行或列,这是有用的。
设置位置
在 GridPane 中定位内容可以通过四种方式实现:
设置放置节点的单元格
更改列或行的大小
将节点设置为跨多个列或行
更改列和行之间的填充
1. 设置放置节点的单元格
在最粗糙的层次上,通过在网格中设置节点的位置来实现节点的定位。这可以通过同时设置行位置、列位置或两者来实现。
//set both positions simultaneously
GridPane.setConstraints(myNode, 0, 1);
//set column position
GridPane.setColumnIndex(myNode, 0);
//set row position
GridPane.setRowIndex(myNode, 1);
2. 更改列或行的大小
默认情况下,列和行大小将根据内容的首选宽度和高度计算。可以通过创建 ColumnConstraints 和 RowConstraints 对象重写此行为,并将它们应用到 GridPane。
使用 RowConstraints 对象,可以使用 setPrefHeight(Double value)
指定行的高度为像素尺寸,或者使用setPercentHeight(Double value)
指定的网格窗格总高度的百分比。
ColumnConstraints 对象也有类似的方法。
3. 将节点设置为跨多个列或行
可以通过指示 GridPane 将节点设置为跨多列或多行来更改节点在网格中的位置。
通过静态方法来设置:GridPane.setRowSpan(Node child, Integer value)
、GridPane.setColumnSpan(Node child, Integer value)
。
4. 更改列和行之间的填充
最后,可以通过更改 GridPane 的行和列之间的填充来调整节点的位置。
GridPane 边缘的普通填充可以使用 setHgap(double value)
和 setVgap(double value)
,其他地方则可以使用 setPadding(Insets value)
来设置。
设置大小
可以通过多种方式成功地调整节点和列的大小。这两种方法都需要创建 RowConstraints 和 ColumnConstraints 对象。
扩展行和列以填充未占用的空间
垂直和水平地将节点扩展到行或列中的可用空间
默认情况下,GridPane 将以节点的首选大小容纳节点,并且不会将节点拉伸以填充列。它还将把所有列和行相等地展开到可用空间中,因此这对于调优位置行为很有用。
1. 扩展行和列以填充未占用的空间
可以通过创建一个 RowConstraints 对象并调用 setVgrow(true)
来指示一行填充任何未被占用的空间。
同样,可以通过创建 ColumnConstraints 对象并调用 setHgrow(true)
来指示列填充任何未占用的空间。
空间通过以下逻辑分布在行和列之间:
- 总是在所有具有
Priority.ALWAYS
优先级的行或列之间均匀地分配空间。 - 如果没有列/行具有
Priority.ALWAYS
优先级。如果仍然存在剩余空间,则将剩余空间分配到带有Priority.SOMETIMES
优先级的行或列之间。
2. 垂直和水平地将节点扩展到行或列中的可用空间
通过创建一个 RowConstraints 对象(或修改一个现有的对象),并调用 setFillHeight (true)
,可以指示一行将所有节点垂直增长到未占用的空间。
同样以类似的方式,可以通过创建 ColumnConstraints 对象(或修改现有的对象)并调用 setFillWidth (true)
来指示列填充任何未被占用的水平空间。
设置对齐
最后,可以指示行内的节点垂直对齐(以及列内的节点水平对齐)。这些是使用 HPos 和 VPos 枚举设置的。
最后,可以通过创建 RowConstraints 对象(或修改现有对象)并调用 setValignment (VPos value)
来指示一行垂直对齐节点。
通过创建 ColumnConstraints 对象(或修改现有对象)并调用 setHalignment (HPos value)
,可以指示列水平对齐节点。
TilePane
TilePane 通过在一维中添加 tiles(节点在其中呈现的虚拟区域),然后将 tiles 包装到其他行或列来计算其布局。tiles 在整个窗格中大小一致。
应用方式
可以通过调整 tile 之间的填充或设置特定于节点的边距来改变 tile 中节点的位置、大小和对齐方式。
设置位置
节点之间的距离可以通过设置包含节点的 TilePane 实例的 hgap 和 vgap 属性来调整。
Tile 的大小也可以作为一种改变节点位置的方法,尽管用户应该知道这也会改变节点的大小(在其可调整大小的范围内)。
Tile 的大小可以通过调用 setPrefTileWidth(double value)
和 setPrefTileHeight(double value)
来改变。这些大小不能保证,如果 TilePane 的大小减小到不能容纳这些瓷砖的程度,那么它们的大小就会减小。
设置大小
与许多其他布局不同,TilePane 在默认情况下会尝试在其可调整大小范围内拉伸小于 tile 大小的 tile 实例。
如果 tileprefWidth
和 tilePrefHeight
属性小于节点的首选尺寸,它也会尝试在可调整大小的范围内缩小较大的节点。
设置对齐
如果默认的 tile 大小大于任何节点的最大大小,节点将根据它设置的对齐方式在 tile 中分布。
使用静态方法 TilePane.setAlignment(Pos value)
在每个节点的基础上设置对齐。默认的对齐方式是 Pos.CENTER
,但如果您使用静态方法 TilePane.getAlignment(Node node)
在没有显式设置的节点上,它将返回 null(对于其他窗格也是如此)。
FlowPane
FlowPane 的设计是为了给你的布局一个响应式的感觉,改变行或列的长度,以适应流窗格本身的大小。对于外观相似但需要重新洗牌的物品来说,这是非常棒的。
应用方式
默认情况下,FlowPane 会将节点填充到行中,然后在必要时生成额外的行。对于生成内容列而不是行的流窗格,创建一个流窗格并调用 setOrientation(Orientation.VERTICAL)
。
流窗格不会尝试调整节点的大小,所以这里没有调整大小的部分。
设置位置
因为 FlowPane 可以用来将项目打包成行或列,所以它在内部将它们称为“runs”。如果方向是水平的,则运行是水平的。如果它是垂直的,runs 就是垂直的。
FlowPanes 的基本规则是:在下一个节点超过最大允许长度之前尽可能长地运行一次,然后将该节点作为下一次运行的第一项。
你可以通过调用 setMargin(Insets Insets)
来设置 FlowPane 的边距,在内容周围创建一个空间,它不会试图填充节点。它使用 Insets 对象,该对象可以分别或同时指定顶部、底部、左侧和右侧的边距。
您可以使用 setAlignment(Pos value)
来确定 FlowPane 将分配的剩余空间的位置。上面的图像显示了一个垂直的流窗格,对齐方式为 Pos.TOP_LEFT
,这意味着空间分布在底部和右侧。
Run 的长度
您可以通过调用 setPrefWrapLength(Double value)
来控制 run 的长度,无论您设置的方向如何,它都可以工作。
垂直与水平间隙
当设置间隙时,FlowPane 不区分方向,所以它们是绝对的。这意味着需要使用 setVgap(Double value)
设置垂直元素之间的间隙,使用 setHgap(Double value)
设置水平元素之间的间隙。
如果您希望功能区分运行和运行中的项目,那么开始切换方向时可能会感到困惑。
run 中元素的间隙
对于水平 FlowPane(默认值),使用 setVgap()
,对于垂直流窗格,使用 setHgap()
。
run 之间的间隙
对于水平 FlowPane(默认值),使用 setHgap()
,对于垂直流窗格,使用 setVgap()
。
设置对齐
如果 FlowPane 中包含不同大小的节点,则流窗格将运行以容纳该运行中的最大成员。这意味着如果它是一个垂直的流窗格,它将使运行足够宽的最宽成员。如果它是一个水平的 FlowPane,它将使运行足够高的最高成员。
令人沮丧的是,FlowPane 要求您分别设置列(垂直方向)和行(水平方向)的对齐方式。
- 水平
FlowPane
带有行:setRowValignment(VPos value).
VPos 枚举值为TOP
,BOTTOM
,BASELINE
andCENTER
。 - 垂直
FlowPane
带有列:setColumnHalignment(HPos value).
HPos 枚举值为LEFT
,CENTER
andRIGHT
。
这是一个垂直方向的 FlowPane,使用 HPos.LEFT
设置列对齐。
HBox
HBox 从左到右水平定位节点。可以设置节点大小和对齐首选项,而元素的顺序可以通过改变 ObservableList 子元素的顺序来改变。
应用方式
与FlowPane不同,HBox 只有一组水平节点。然而,她们对孩子的体型有更多的控制权。
设置位置
子节点的水平顺序是从左到右的,与 ObservableList 中项目的顺序相同。默认情况下,节点的顺序是添加子节点的顺序。
设置大小
可以在水平方向和垂直方向上对 HBox 进行调整。
垂直方向
默认情况下,HBox 将垂直调整其子节点的最大允许高度(由 HBox 本身的高度及其最大高度属性限制)。这个功能可以通过调用 setFillHeight(false)
来关闭。
这是为 HBox 实例设置的,并将应用于该 HBox 中的所有节点。
水平方向
节点可以请求按节点逐个调整大小,以填充任何额外的水平空间。
节点可以通过调用静态方法 Hbox.setHgrow(Node node, Priority value)
来请求填充 HBox 内部任何剩余的水平空间。
Priority 枚举值为 SOMETIMES、ALWAYS 和 NEVER 。空间的分布方式如下:
- 总是在所有具有优先级的节点之间均匀分配空间。
- 如果没有节点具有优先级。如果总是有剩余空间,则在节点之间优先分配剩余空间。
- 根据 HBox 的对齐方式分配剩余空间。
设置对齐
你可以通过调用 setAlignment(Pos value)
来设置 HBox 的对齐方式。
VBox
VBox 从上到下垂直地定位节点。我将简单介绍一下,但如果您想了解更详细的功能指南,可以参考 HBox 的部分,内部逻辑是相同的,但是是水平的而不是垂直的。
应用方式
VBox 有一组垂直节点。就像 HBox 一样,比垂直的 FlowPane 对其子节点的大小有更多的控制。
设置大小
可以水平和垂直地对VBox进行调整。
水平设置
要停止 VBox 水平拉伸其子元素,使用 setFillWidth(false)
。这是为 VBox 实例设置的,并将应用于该 VBox 中的所有节点。
垂直设置
通过通过方法 Vbox.setHgrow(Node node, Priority value)
请求节点伸展以适应 VBox 中任何剩余的垂直空间。空间的分布规则与 HBox 相同(但垂直分布……)
设置对齐
可以通过方法 setAlignment(Pos value)
设置对齐方式。
总结
JavaFX 有 9 个预定义的容器,提供了定义图表、图像和控件等节点的位置、大小和对齐方式的功能。JavaFX 的布局可以实现各种各样的功能和位置控制。
有三种基本的布局类型:网格、行列和块。它们定义了布局“思考”节点定位的方式。先画出容器的草图,然后用它来决定你需要的布局类型,这通常是一个很好的开始。
一旦你选择了你想让你的面板看起来的基本方式(网格,行列或块),我建议你选择你的布局基于他们如何响应被调整大小。除非你明确地将它们设置为固定大小,否则用户总是会发现一些漏洞,使窗口变得非常大(或非常小)。
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.