使用 GridPane 为不同行中的每个单元格实现唯一的列宽?

Achieve Unique Column Width for each Cell in Different Rows with a GridPane?

我正在尝试使用 GridPane:

JavaFx 中对信用卡数据建模

我的模型包含 3 行(注意:每个字段由标签 + 文本字段组成):

第 1 行:名字和姓氏(4 个字段)

第 2 行:信用卡号(2 个字段)

第 3 行:到期日期 - 月、年 + CVV(6 个字段)

见下方截图:

我正在阅读 this tutorial 其中指出:

All cells in the same row will have the same height, and all cells in the same column will have the same width. Different rows can have different heights and different columns can have different widths.

是否有任何解决方法可以在 GridPane 中逐行设置不同大小的列?

对于图像中的特定布局,我将使用 VBoxHBox 行:

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();
    }

}

输出:

+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;
    }
}