【问题标题】:JavaFX - force the child of a GridPane to fit the size of the cellJavaFX - 强制 GridPane 的子级适应单元格的大小
【发布时间】:2018-07-11 00:17:53
【问题描述】:

我试图强制GridPane 的子元素适应它所在单元格的大小。在这种情况下,我正在创建一个月视图日历,并且列和行是一个固定大小,无论是什么在里面。

在此日历上,每个单元格都包含一个普通的VBox,每个VBox 都包含一个标签,用于显示当月中的某天和当天的每个事件。许多日子没有事件;其中一些有一个事件;还有一些有不止一个事件。

日历大小取决于窗口大小,会随着窗口大小而增长和缩小。现在,如果单元格太小而无法容纳所有事件,那么单元格中当天VBox 的高度就会大于单元格。

标题行有以下约束:

HEADER_CONSTRAINT = new RowConstraints(10, -1, 500, 
        Priority.NEVER, VPos.BASELINE, true);

其他行有这个限制:

ROW_CONSTRAINT = new RowConstraints(30, 30, Integer.MAX_VALUE,
        Priority.ALWAYS, VPos.TOP, true);

我认为我需要做的是:

grid.add(cell, c, r);
vbox.maxHeightProperty().bind(grid.getRow(r).heightProperty()); // <-- this line is not right, but something like this.

【问题讨论】:

  • 您确定您使用的是HBoxes 而不是VBoxes。顺便说一句:不管minHeightmaxHeight 属性到底如何,GridPane 不会强制VBox 将大小调整为低于minHeight 方法返回的值。由于VBox 通过将其托管子项的最小高度(加上一些间距)相加来确定此方法的结果,并返回此值的最大值以及设置为minHeight 属性的值(不考虑该属性的特殊值)。
  • 哦。你说的对。 VBox.
  • 你之前已经问过这个问题了。我已经说过了 - 不要试图让 GridPane 的子级大小自己到其父级 GridPane,这会导致循环关系。您需要创建一个minimal reproducible example。我很确定这个问题是由场景图中的其他东西引起的,不告诉其他地方发生了什么不会让我们找出问题所在。
  • 我看到了您的评论,但没有帮助,所以我想我会再试一次。对我来说,这绝不是一种选择,这似乎很奇怪。我不认为这会导致循环关系,因为我有一个行约束,我认为它给了它一个固定的高度;虽然我可能错了。
  • 如果您的行具有单个固定数字的最小/首选项/最大高度,则不会有任何循环关系。再试一次也无济于事——你没有设法从任何人那里得到一个可行的解决方案,因为你没有发布其他人可以找出问题所在的东西。如果我真的不得不猜测,那么您可能在 VBox 中使用了 Text 类(而不是 Label 类)。但是,这只是猜测。

标签: java javafx


【解决方案1】:

正如@fabian 正确提到的,VBox 的大小完全取决于其子项的大小。如果您希望拥有一个不根据其子级调整大小的容器,您可以使用 Pane 代替。像这样:

public void start(Stage primaryStage)
{
    GridPane root = new GridPane();
    Scene scene = new Scene(root, 300, 300);
    int c = 0, r = 0;

    Pane c0 = new Pane();
    c0.setPrefSize(200, 200);
    c0.setBorder(new Border(new BorderStroke(Color.RED, BorderStrokeStyle.SOLID, CornerRadii.EMPTY,
            new BorderWidths(1))));
    root.add(c0, c, r);

    primaryStage.setScene(scene);
    primaryStage.show();
}

添加要测试的形状:

Pane c0 = new Pane();
c0.setPrefSize(200, 200);
c0.setBorder(new Border(new BorderStroke(Color.RED, BorderStrokeStyle.SOLID, CornerRadii.EMPTY,
        new BorderWidths(1))));
{
    Circle circle = new Circle(160, Color.BLUE);
    circle.setCenterX(160);
    circle.setCenterY(160);
    c0.getChildren().add(circle);
}
root.add(c0, c, r);

请注意,虽然形状突出到c0 的边界之外,但c0 的边界保持不变,表明它的大小不受影响。为防止内容突出,需要添加剪辑:

c0.setClip(new Rectangle(c0.getPrefWidth(), c0.getPrefHeight()));

如果您想要更花哨的剪辑而不是简单的修剪,例如淡入、淡出和阴影,您可以阅读这个非常好的教程here

现在只需将这个Circle 替换为您的VBox,这样VBox 就是这个Pane 的一个子代,然后将Pane 添加到您的GridPane 中就完成了。我将在这里提供我的最终代码作为参考:

public void start(Stage primaryStage)
{
    GridPane root = new GridPane();
    Scene scene = new Scene(root, 300, 300);
    int c = 0, r = 0;

    Pane c0 = new Pane();
    c0.setPrefSize(200, 200);
    c0.setClip(new Rectangle(c0.getPrefWidth(), c0.getPrefHeight()));
    c0.setBorder(new Border(new BorderStroke(Color.RED, BorderStrokeStyle.SOLID, CornerRadii.EMPTY,
            new BorderWidths(1))));
    {
        Circle circle = new Circle(160, Color.BLUE);
        circle.setCenterX(160);
        circle.setCenterY(160);
        c0.getChildren().add(circle);
    }
    root.add(c0, c, r);

    primaryStage.setScene(scene);
    primaryStage.show();
}

更新:

@Jai 指出在这个实现中大小是静态的。如果您希望在GridPane 的大小发生变化时动态调整单元格的大小,您可以向其widthPropertyheightProperty 添加一个侦听器,如下所示:

int rowCount = 5, columnCount = 7; // You should know these values.
ChangeListener<Number> updater = (o, oV, nV) ->
{
    double newWidth = root.getWidth() / columnCount;
    double newHeight = root.getHeight() / rowCount;
    root.getChildren().forEach(n ->
    {
        // Assuming every child is a Pane.
        Pane p = (Pane) n;
        p.setPrefSize(newWidth, newHeight);
        p.setClip(new Rectangle(newWidth, newHeight));
    });
};
root.widthProperty().addListener(updater);
root.heightProperty().addListener(updater);

或者...

如果您希望使用ColumnConstraintsRowConstraints 来确定单元格大小,您可以将Panes 设置为展开,并且仅在侦听器中更新它们的剪辑,现在侦听器将侦听ColumnConstraints 和@987654347 @:

ColumnConstraints cc = new ColumnConstraints(200);
RowConstraints rc = new RowConstraints(200);
c0.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
c0.setClip(new Rectangle(cc.getPrefWidth(), rc.getPrefHeight()));

还有……

ChangeListener<Number> updater = (o, oV, nV) ->
{
    root.getChildren().forEach(n ->
    {
        // Assuming every child is a Pane.
        Pane p = (Pane) n;
        p.setClip(new Rectangle(cc.getPrefWidth(), rc.getPrefHeight()));
    });
};
cc.prefWidthProperty().addListener(updater);
rc.prefHeightProperty().addListener(updater);

【讨论】:

  • 我不建议任何人为此使用PanePane 所做的只是抱孩子——它不做任何类型的尺寸管理。这样做意味着 OP 应该在整个场景图中硬编码静态尺寸。
  • @Jai 不一定。您始终可以为容器的宽度和高度添加自定义侦听器,您可以在其中更新Panes 及其剪辑的大小。这有点乏味,但是对于您希望每个单元格大小相同的特定用例(日历),我认为这个解决方案已经足够了。
猜你喜欢
  • 2013-10-29
  • 1970-01-01
  • 2014-11-12
  • 2018-12-20
  • 2016-09-30
  • 2020-12-05
  • 1970-01-01
  • 2016-01-12
  • 2013-01-23
相关资源
最近更新 更多