使用 GridPane 为不同行中的每个单元格实现唯一的列宽?
Posted
技术标签:
【中文标题】使用 GridPane 为不同行中的每个单元格实现唯一的列宽?【英文标题】:Achieve Unique Column Width for each Cell in Different Rows with a GridPane? 【发布时间】:2022-01-02 10:00:50 【问题描述】:我正在尝试使用GridPane
对JavaFx
中的信用卡数据进行建模:
我的模型包含 3 行(注意:每个字段由标签 + 文本字段组成):
第 1 行:名字和姓氏(4 个字段)
第 2 行:信用卡号(2 个字段)
第 3 行:到期日期 - 月、年 + CVV(6 个字段)
请看下面的截图:
我正在阅读this tutorial,其中指出:
同一行中的所有单元格将具有相同的高度,并且同一行中的所有单元格将具有相同的高度 同一列将具有相同的宽度。不同的行可以有 不同的高度和不同的列可以有不同的宽度。
是否有任何解决方法可以在 GridPane
中逐行设置不同大小的列?
【问题讨论】:
这个特定的设计看起来好像三个HBox
s 放在VBox
中会更好。您也可以使用一些巧妙的列跨度应用来实现同样的效果。
@James_D 听起来你在做一些好事。您能否详细解释一下您的意思 您也可以使用列跨度的一些巧妙应用来实现同样的目的 也许作为一个完整的答案?谢谢!!
下次我回到电脑前我会测试一下。
我建议使用 SceneBuilder 来创建更复杂的布局,而不是用代码编写。
顺便说一句:希望此表单不适合在实际应用程序中使用:从 ui 设计的角度来看,它已被破坏;) 需要许多不相关的列/行跨度几乎总是一个明确的警告信号。当它闪烁时,是时候对设计师场景进行一些研究了,有选择:)
【参考方案1】:
对于图像中的特定布局,我会使用 VBox
和 HBox
来表示行:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
public class App extends Application
@Override
public void start(Stage stage)
Label lblFirst = new Label("First");
Label lblLast = new Label("Last");
Label lblNumber = new Label("Card Number");
Label lblMonth = new Label("Month");
Label lblYear = new Label("Year");
Label lblCVV = new Label("CVV");
TextField txtFirst = new TextField();
TextField txtLast = new TextField();
TextField txtNumber = new TextField();
TextField txtMonth = new TextField();
TextField txtYear = new TextField();
TextField txtCVV = new TextField();
HBox row1 = new HBox(10);
HBox row2 = new HBox(10);
HBox row3 = new HBox(10);
row1.getChildren().add(createCell(lblFirst, txtFirst));
row1.getChildren().add(createCell(lblLast, txtLast));
row2.getChildren().add(createCell(lblNumber, txtNumber));
row3.getChildren().add(createCell(lblMonth, txtMonth));
row3.getChildren().add(createCell(lblYear, txtYear));
row3.getChildren().add(createCell(lblCVV, txtCVV));
VBox rows = new VBox(10, row1, row2, row3);
StackPane.setMargin(rows, new Insets(10));
StackPane root = new StackPane(rows);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
private static HBox createCell(Label label, TextField text)
HBox pane = new HBox(5, label, text);
label.setMinWidth(Pane.USE_PREF_SIZE);
text.setMinWidth(50);
pane.setAlignment(Pos.CENTER);
HBox.setHgrow(pane, Priority.ALWAYS);
HBox.setHgrow(text, Priority.ALWAYS);
return pane;
public static void main(String[] args)
launch();
输出:
【讨论】:
不要硬编码大小提示【参考方案2】:+1 @JamesD 的建议和@Oboe 的回答。理想情况下,我会分离每一行布局,以便以简单的方式处理它,而不是仅使用一个 GridPane 使其变得复杂。
话虽如此,如果您想了解或了解如何使用一个 GridPane 进行类似的布局,下面的实现可能会给您一个快速的想法。
首先将您的布局拆分为所需的列,以确定您需要多少列。 (如下图)
现在您将知道哪个节点将位于哪个列以及它将占据多少列(colspan)
我将针对一个节点进行解释:
假设您要插入名字的字段。如果你注意到图片中,它在rowIndex:0,columnIndex:1,它占据了4列,所以colSpan的值为4。这里我们没有合并任何行,所以rowSpan的值总是1。
pane.add(getField(), 1, 0, 4, 1); // node, colIndex, rowIndex, colSpan, rowSpan
同样,您可以关联其余的节点布局。而且为了更精确,您可以使用 ColumnConstraints 设置每列的首选宽度。下面是布局和约束的完整代码:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CreditCardPaneDemo extends Application
@Override
public void start(Stage stage) throws Exception
VBox root = new VBox();
root.setPadding(new Insets(5));
root.setSpacing(10);
Scene scene = new Scene(root,300,200);
stage.setScene(scene);
stage.setTitle("CreditCard");
stage.show();
GridPane pane = new GridPane();
pane.setStyle("-fx-border-color:black;-fx-border-width:1px;-fx-background-color:yellow");
pane.setPadding(new Insets(5));
pane.setHgap(5);
pane.setVgap(5);
pane.add(getLabel("First"), 0, 0, 1, 1);
pane.add(getField(), 1, 0, 4, 1);
pane.add(getLabel("Last"), 5, 0, 1, 1);
pane.add(getField(), 6, 0, 2, 1);
pane.add(getLabel("Card Number"), 0, 1, 3, 1);
pane.add(getField(), 3, 1, 5, 1);
pane.add(getLabel("Month"), 0, 2, 2, 1);
pane.add(getField(), 2, 2, 2, 1);
pane.add(getLabel("Year"), 4, 2, 1, 1);
pane.add(getField(), 5, 2, 1, 1);
pane.add(getLabel("CVV"), 6, 2, 1, 1);
pane.add(getField(), 7, 2, 1, 1);
pane.getColumnConstraints().addAll(getCc(70), getCc(20), getCc(80), getCc(20), getCc(25), getCc(90), getCc(80), getCc(100));
CheckBox gridLines = new CheckBox("Show grid lines");
gridLines.selectedProperty().addListener((obs, old, val) -> pane.gridLinesVisibleProperty().set(val));
root.getChildren().addAll(gridLines, pane);
private ColumnConstraints getCc(double width)
ColumnConstraints cc = new ColumnConstraints();
cc.setPrefWidth(width);
return cc;
private Label getLabel(String txt)
Label lbl = new Label(txt);
lbl.setMinWidth(Region.USE_PREF_SIZE);
return lbl;
private TextField getField()
TextField field = new TextField();
field.setMaxWidth(Double.MAX_VALUE);
return field;
【讨论】:
不要硬编码大小提示 @kleopatra,感谢您的建议。如果我理解正确,您提到的是 USE_PREF_SIZE 和 MAX_VALUE 的使用。正确的?如果是,您能否建议使用它们的替代方法,因为我找不到确保标签具有不显示“...”的最小宽度以及自动拉伸可用空间的文本框的方法。如果不是,你能解释一下你所说的提示吗? 不,我的意思是传递给getCc
的硬编码实数——Region 中定义的哨兵和数谱的边界都可以:)
但是,从视觉设计的角度来看,这种布局是病态的——它打破了关于如何设计一个好的用户界面的每一条规则。所以希望这只是学习网格窗格的用法和限制的练习;)以上是关于使用 GridPane 为不同行中的每个单元格实现唯一的列宽?的主要内容,如果未能解决你的问题,请参考以下文章