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

Posted

技术标签:

【中文标题】使用 GridPane 为不同行中的每个单元格实现唯一的列宽?【英文标题】:Achieve Unique Column Width for each Cell in Different Rows with a GridPane? 【发布时间】:2022-01-02 10:00:50 【问题描述】:

我正在尝试使用GridPaneJavaFx 中的信用卡数据进行建模:

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

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

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

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

请看下面的截图:

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

同一行中的所有单元格将具有相同的高度,并且同一行中的所有单元格将具有相同的高度 同一列将具有相同的宽度。不同的行可以有 不同的高度和不同的列可以有不同的宽度。

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

【问题讨论】:

这个特定的设计看起来好像三个HBoxs 放在VBox 中会更好。您也可以使用一些巧妙的列跨度应用来实现同样的效果。 @James_D 听起来你在做一些好事。您能否详细解释一下您的意思 您也可以使用列跨度的一些巧妙应用来实现同样的目的 也许作为一个完整的答案?谢谢!! 下次我回到电脑前我会测试一下。 我建议使用 SceneBuilder 来创建更复杂的布局,而不是用代码编写。 顺便说一句:希望此表单不适合在实际应用程序中使用:从 ui 设计的角度来看,它已被破坏;) 需要许多不相关的列/行跨度几乎总是一个明确的警告信号。当它闪烁时,是时候对设计师场景进行一些研究了,有选择:) 【参考方案1】:

对于图像中的特定布局,我会使用 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();
    


输出:

【讨论】:

不要硬编码大小提示【参考方案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 为不同行中的每个单元格实现唯一的列宽?的主要内容,如果未能解决你的问题,请参考以下文章

javafx GridPane 检索特定的单元格内容

JavaFX:将图像插入 GridPane

查找最小数值并返回不同行中的单元格

PHP MySQL将来自不同行的单元格组合在一起具有相同的值

JavaFX中GridPane的行居中对齐

基于在 Excel 中的每一行中为不确定的行数确定的值的条件格式