DOC-05-02 调整节点大小和对齐的技巧
本文主要介绍在JavaFX的布局面板中如何设置节点的大小和对齐方式。
使用JavaFX内置布局面板的一个主要好处在于节点的大小和对齐方式是由布局面板来控制的。当布局面板的大小变化时,节点也会根据其预设大小的范围来调整大小。请注意并不是所有的节点都可以改变大小。UI控件(Control)和布局面板(Layout Pane)是可以调整大小的。但是形状(Shape)、文本(Text)以及组(Group)是不可以调整大小的,它们在布局中被认为是刚性对象(Rigid Objects)。
如果你希望对UI中的控件大小有更多的控制,你可以直接设置它们的预设大小(Preferred Size)范围。然后布局面板就会根据你的设置来计算和决定控件的大小。如果希望管理控件的位置,你可以使用布局面板的对齐属性(Alignment)属性。
本文提供了一些在面板中设置节点大小和对齐方式的简单示例。LayoutSizingAligning.java(见附录B)文件中包含了本文中的示例代码。LayoutSizingAligning.zip文件包含了这些示例的Netbeans IDE工程。
调整节点大小
布局需要通过调用prefWidth(height)和prefHeight(width)方法来得到节点的预设大小 (Preferred Size)。在默认情况下,UI控件会根据其所包含的内容来自动计算预设大小属性的默认值。例如,按钮(Button)被计算出来的大小取决于标签文本的长度和字体大小,再加上按钮图标的大小。一般来说,计算出来的大小刚好能让控件及其标签完全显示可见。
UI控件根据其典型用途还提供了默认的最小和最大值。例如,Button的默认最大值就是其预设大小,因为一般来说你不会想让按钮任意变大。然而ScrollPane的最大值却是不受限制的,因为一般来说你会希望它们能够变大并充满所有的可用空间。
你可以使用节点的默认大小,或者通过设置大小值来提供你所需要的外观。图2-1显示了在一个BorderPane中的几个Button和一个ListView的默认大小。
图2-1计算大小
假设你期望的界面如图2-2所示,其中UI控件根据设置的约束值改变了大小。
图2-2改变大小后的效果图
应用程序经常需要直接设置控件大小的最小值、预设值和最大值。接下来的章节就向你介绍一些小技巧,教你重写计算大小的方法来得到你想要的界面效果。
设置多个按钮(Button)大小一致
你可以先确定每个按钮的高度和宽度,然后将它们同时设置给预设大小和最大大小,但这很麻烦。另一个更简单的办法就是让布局面板来控制。选用何种布局面板取决于你想实现的效果。
使用垂直盒子(VBox)
图2-1中的右边区域使用了一个VBox,其中包含的几个按钮都是使用默认计算的大小(Computed Size)。这些按钮已经有了相同的高度,因此我们现在仅仅需要设置其宽度一致。
图2-2中依然是使用VBox,但是利用了它的一个默认行为——VBox的宽度与其包含的所有组件的最大预设宽度一致。为了让所有的按钮能够自动保持和VBox一样的宽度,我们将每个按钮的最大宽度(MaxWidth)设置为常量Double.MAX_VALUE,这个设置可以使控件宽度不受限制地增长。另一种做法就是将各个按钮的最大宽度设置为一个指定的常量,例如80.0。
例2-1展示了如何设置VBox中的一列按钮拥有同样的宽度。
例2-1设置一列按钮宽度一致
Java
BorderPane border = new BorderPane();
border.setPadding(new Insets(20, 0, 20, 20));
Button btnAdd = new Button("Add");
Button btnDelete = new Button("Delete");
Button btnMoveUp = new Button("Move Up");
Button btnMoveDown = new Button("Move Down");
btnAdd.setMaxWidth(Double.MAX_VALUE);
btnDelete.setMaxWidth(Double.MAX_VALUE);
btnMoveUp.setMaxWidth(Double.MAX_VALUE);
btnMoveDown.setMaxWidth(Double.MAX_VALUE);
VBox vbButtons = new VBox();
vbButtons.setSpacing(10);
vbButtons.setPadding(new Insets(0, 20, 10, 20));
vbButtons.getChildren().addAll(btnAdd, btnDelete, btnMoveUp, btnMoveDown);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
BorderPaneborder=newBorderPane();
border.setPadding(newInsets(20,0,20,20));
ButtonbtnAdd=newButton("Add");
ButtonbtnDelete=newButton("Delete");
ButtonbtnMoveUp=newButton("Move Up");
ButtonbtnMoveDown=newButton("Move Down");
btnAdd.setMaxWidth(Double.MAX_VALUE);
btnDelete.setMaxWidth(Double.MAX_VALUE);
btnMoveUp.setMaxWidth(Double.MAX_VALUE);
btnMoveDown.setMaxWidth(Double.MAX_VALUE);
VBoxvbButtons=newVBox();
vbButtons.setSpacing(10);
vbButtons.setPadding(newInsets(0,20,10,20));
vbButtons.getChildren().addAll(btnAdd,btnDelete,btnMoveUp,btnMoveDown);
在本教程的示例中,使用了一个BorderPane进行布局。其中有一列按钮被放到了BorderPane的右边区域中来将它们的宽度限制为最宽按钮的预设宽度。如果你将VBox面板放到BorderPane的中间区域,而中间区域将会扩张填充所有可用空间,那么VBox及其包含的按钮也都将随之扩张。
使用磁贴面板(TilePane)
图2-1中使用了一个HBox来管理下方的几个按钮,并对它们采用了默认的计算大小值(Computed Size)。每个按钮都有不同的宽度和高度。
图2-2中则使用一个水平的TilePane来管理按钮,并利用了它的特性——每个单元格(Tile)的大小一致。每块Tile的大小由所有按钮的最大优先宽度和高度决定。
为了使按钮的大小能够适应Tile的大小变化,我们需要设置按钮的最大宽度和高度为常量Double.MAX_VALUE。
例2-2展示了使用TilePane来使得一行按钮都具有同样的宽度和高度。
例2-2设置一行按钮的大小一致
Java
Button btnApply = new Button("Apply");
Button btnContinue = new Button("Continue");
Button btnExit = new Button("Exit");
btnExit.setStyle("-fx-font-size: 15pt;");
btnApply.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
btnContinue.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
btnExit.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
TilePane tileButtons = new TilePane(Orientation.HORIZONTAL);
tileButtons.setPadding(new Insets(20, 10, 20, 0));
tileButtons.setHgap(10.0);
tileButtons.setVgap(8.0);
tileButtons.getChildren().addAll(btnApply, btnContinue, btnExit);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ButtonbtnApply=newButton("Apply");
ButtonbtnContinue=newButton("Continue");
ButtonbtnExit=newButton("Exit");
btnExit.setStyle("-fx-font-size: 15pt;");
btnApply.setMaxSize(Double.MAX_VALUE,Double.MAX_VALUE);
btnContinue.setMaxSize(Double.MAX_VALUE,Double.MAX_VALUE);
btnExit.setMaxSize(Double.MAX_VALUE,Double.MAX_VALUE);
TilePanetileButtons=newTilePane(Orientation.HORIZONTAL);
tileButtons.setPadding(newInsets(20,10,20,0));
tileButtons.setHgap(10.0);
tileButtons.setVgap(8.0);
tileButtons.getChildren().addAll(btnApply,btnContinue,btnExit);
当窗口大小变化时,Tile的大小不会跟着变化,因此其中的按钮大小也不会变化。注意一旦窗口被缩小了,在TilePane中的按钮会改变位置,但并不会随之缩小。
保持节点大小为其预设大小
当一个舞台(stage)被改变大小时,stage中的布局面板可能会有更多或者更少的空间来分配给其包含的控件。根据每个控件大小的最小值、预设值和最大值范围,每种布局面板都有自己的空间分配规则。
一般来说,默认最大大小值为Double.MAX_VALUE的控件会扩展以填满可用空间,而指定了最大大小的控件则不会。例如,一个ListView对象有一个不受限制的最大大小。如果你想限制其高度为其预设大小,你可以设置其最大大小为常量Control.USE_PREF_SIZE,如例2-3所示。
例2-3设置最大高度为预设高度(Preferred Height)
Java
ListView lvList = new ListView<>();
ObservableList items = FXCollections.observableArrayList (
"Hot dog", "Hamburger", "French fries",
"Carrot sticks", "Chicken salad");
lvList.setItems(items);
lvList.setMaxHeight(Control.USE_PREF_SIZE);
1
2
3
4
5
6
ListViewlvList=newListView<>();
ObservableListitems=FXCollections.observableArrayList(
"Hot dog","Hamburger","French fries",
"Carrot sticks","Chicken salad");
lvList.setItems(items);
lvList.setMaxHeight(Control.USE_PREF_SIZE);
默认情况下,按钮只会增长到其预设大小。但是,如果其最小宽度没有被重设,则按钮可能会收缩到标签只显示三个点(…)。为了防止按钮变得比其预设宽度更小,可以将它的最小宽度设置为其预设宽度,如例2-4所示。
例2-4设置最小宽度为预设宽度
Java
Button btnMoveDown = new Button("Move Down");
btnMoveDown.setMinWidth(Control.USE_PREF_SIZE);
1
2
ButtonbtnMoveDown=newButton("Move Down");
btnMoveDown.setMinWidth(Control.USE_PREF_SIZE);
控件的预设大小初始时基于计算大小值(Computed Size)。你也可以通过设置你需要的预设大小来覆盖默认预设值。下面的语句重设了ListView的预设宽度:
Java
lvList.setPrefWidth(150.0);
1
lvList.setPrefWidth(150.0);
阻止改变大小
如果你不想节点的大小发生改变,则可以将其最小、最大和预设大小设置为相同值。若是仅仅想限制高度或者宽度不变,同理将高度或者宽度的约束值设置为相同值。在例2-5中,创建了一个高度和宽度约束值都设置成为一致的ListView,所以当窗体的大小发生改变时ListView不会改变。其中还创建了一个所有宽度约束值被设置成一致的按钮。
例2-5设置大小约束值来阻止改变大小
Java
ListView lvList = new ListView();
lvList.setMinSize(150.0, Control.USE_PREF_SIZE);
lvList.setMaxSize(150.0, Control.USE_PREF_SIZE);
Button btnDelete = new Button("Delete");
btnDelete.setMinWidth(80.0);
btnDelete.setPrefWidth(80.0);
btnDelete.setMaxWidth(80.0);
1
2
3
4
5
6
7
8
ListViewlvList=newListView();
lvList.setMinSize(150.0,Control.USE_PREF_SIZE);
lvList.setMaxSize(150.0,Control.USE_PREF_SIZE);
ButtonbtnDelete=newButton("Delete");
btnDelete.setMinWidth(80.0);
btnDelete.setPrefWidth(80.0);
btnDelete.setMaxWidth(80.0);
对齐内容
每个布局面板都有一个默认的对齐方式来对齐所包含的节点。例如,在HBox和VBox布局面板中,节点是居上和居左对齐。在TilePane和FlowPane布局面板中,节点会居中对齐。面板自身在默认情况下则是典型的居上和居左对齐方式。
你可以对面板使用setAlignment()方法来控制节点和面板的对齐方式。对齐属性常量有以下枚举类型可选,它们位于javafx.geometry包中:
● HPos – 其值用于指示水平对齐方式
● Pos – 其值用于指示水平和垂直对齐方式。下划线左边的值表示垂直对齐方式,右边的值表示水平对齐方式。例如,BOTTOM_LEFT设置一个节点垂直底端对齐以及水平左对齐。
● VPos – 其值用于指示垂直对齐方式
图2-3展示了例2-6的运行效果。其中没有使用任何对齐设置,布局面板被放置在左上角。
图2-3默认位置
例2-6创建一个使用默认布局的UI
Java
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(12);
HBox hbButtons = new HBox();
hbButtons.setSpacing(10.0);
Button btnSubmit = new Button("Submit");
Button btnClear = new Button("Clear");
Button btnExit = new Button("Exit");
btnSubmit.setStyle("-fx-font-size: 15pt;");
Label lblName = new Label("User name:");
TextField tfName = new TextField();
Label lblPwd = new Label("Password:");
PasswordField pfPwd = new PasswordField();
hbButtons.getChildren().addAll(btnSubmit, btnClear, btnExit);
grid.add(lblName, 0, 0);
grid.add(tfName, 1, 0);
grid.add(lblPwd, 0, 1);
grid.add(pfPwd, 1, 1);
grid.add(hbButtons, 0, 2, 2, 1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
GridPanegrid=newGridPane();
grid.setHgap(10);
grid.setVgap(12);
HBoxhbButtons=newHBox();
hbButtons.setSpacing(10.0);
ButtonbtnSubmit=newButton("Submit");
ButtonbtnClear=newButton("Clear");
ButtonbtnExit=newButton("Exit");
btnSubmit.setStyle("-fx-font-size: 15pt;");
LabellblName=newLabel("User name:");
TextFieldtfName=newTextField();
LabellblPwd=newLabel("Password:");
PasswordFieldpfPwd=newPasswordField();
hbButtons.getChildren().addAll(btnSubmit,btnClear,btnExit);
grid.add(lblName,0,0);
grid.add(tfName,1,0);
grid.add(lblPwd,0,1);
grid.add(pfPwd,1,1);
grid.add(hbButtons,0,2,2,1);
假设你期望的界面如图2-4所示,其中布局面板居中显示,并且改变了控件的默认布局方式。
图2-4期望位置
下面的章节将向你介绍如何覆盖默认的位置。
居中GridPane
使用以下语句将例2-6中的GridPane居中:
Java
grid.setAlignment(Pos.CENTER);
1
grid.setAlignment(Pos.CENTER);
对齐列中的控件
在期望的布局中,Label是右对齐的,而TextField是左对齐的。为了在GridPane中实现这个目标,使用ColumnConstrains类来定义每一列,并设置水平对齐方式。例2-7为例2-6中的GridPane定义了列。
例2-7在GridPane中定义列
Java
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(12);
ColumnConstraints column1 = new ColumnConstraints();
column1.setHalignment(HPos.RIGHT);
grid.getColumnConstraints().add(column1);
ColumnConstraints column2 = new ColumnConstraints();
column2.setHalignment(HPos.LEFT);
grid.getColumnConstraints().add(column2);
1
2
3
4
5
6
7
8
9
10
11
12
GridPanegrid=newGridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(12);
ColumnConstraintscolumn1=newColumnConstraints();
column1.setHalignment(HPos.RIGHT);
grid.getColumnConstraints().add(column1);
ColumnConstraintscolumn2=newColumnConstraints();
column2.setHalignment(HPos.LEFT);
grid.getColumnConstraints().add(column2);
另一种设置控件右对齐的方法就是使用VBox布局面板。使用setAlignment()方法设置如下:
Java
VBox vbox = new VBox;
vbox.setAlignment(Pos.CENTER_RIGHT);
1
2
VBoxvbox=newVBox;
vbox.setAlignment(Pos.CENTER_RIGHT);
居中按钮
按钮被放置在一个HBox中,并跨两列放置在GridPane中。下面的语句将例2-6中的按钮居中对齐:
Java
hbButtons.setAlignment(Pos.CENTER);
1
hbButtons.setAlignment(Pos.CENTER);
HBox的setAlignment()方法将HBox自身在其布局区域中进行居中,并居中其包含的节点。你可能更喜欢,将HBox在一行中居中显示,并设置按钮在HBox中底端对齐,如图2-5所示。
图2-5覆盖位置以及底端对齐按钮
为了按此进行布局,要将HBox面板放到一个只有一个单元格的里层GridPane中,并将这个里层GridPane放置在外层GridPane的第三行中。设置里层GridPane为居中对齐,并设置HBox为底端对齐,如例2-8。
例2-8水平居中和垂直居下按钮
Java
hbButtons.setAlignment(Pos.BOTTOM_CENTER);
hbButtons.getChildren().addAll(btnSubmit, btnClear, btnExit);
GridPane innergrid = new GridPane();
innergrid.setAlignment(Pos.CENTER);
innergrid.add(hbButtons, 0, 0);
grid.add(innergrid, 0, 2, 2, 1);
1
2
3
4
5
6
7
hbButtons.setAlignment(Pos.BOTTOM_CENTER);
hbButtons.getChildren().addAll(btnSubmit,btnClear,btnExit);
GridPaneinnergrid=newGridPane();
innergrid.setAlignment(Pos.CENTER);
innergrid.add(hbButtons,0,0);
grid.add(innergrid,0,2,2,1);
附加资源
想要学习更多关于JavaFX中布局面板的内容,请查看API文档中的javafx.scene.layout包以获取更多信息。
应用程序文件
源代码
NetBeans工程
打赏一下
支付宝
微信