14

如何在 JavaFX 中获取节点的高度或首选高度,我有 3 个VBox,我想将节点添加到最自由的面板,例如:

           Childrens      Total Height of the children's(Sum)
VBoxA          5                     890
VBoxB          4                     610
VBoxC          2                     720

在这种情况下,最自由的是VBoxB,我用这种方法计算最自由的窗格:

private int getFreerColumnIndex() {
    if(columns.isEmpty())
        return -1;

    int columnIndex = 0;
    int minHeight = 0;
    for(int i = 0; i < columns.size(); i++) {
        int height = 0;
        for(Node n : columns.get(i).getChildren()) {
            height += n.getBoundsInLocal().getHeight();
        }

        if(i == 0) {
            minHeight = height;
        } else if(height < minHeight) {
            minHeight = height;
            columnIndex = i;
        }

        if(height == 0)
            break;
    }

    return columnIndex;
}

此方法仅在我当时添加 1 个元素时才有效。但如果我当时添加更多元素:

for (int i = 0; i < 10; i++) {
    SomeNode r1 = new SomeNode ();
    myPane.addElement(r1);
}

该方法getFreerColumnIndex返回相同的索引。这是因为新节点在本地还没有高度。
所以这一行:

height += n.getBoundsInLocal().getHeight(); 

0与新节点一起返回。

任何人都知道如何获得节点的高度?


额外的:

SomeNode延伸的Node

myPane 中的方法 addElement():

public void addElement(final Node element) {
     index = getFreerColumnIndex();
     columns.get(index).getChildren().add(element);
}

额外2:

假设我们有 3 个 vbox: 之前:

 A      B      C
 |      |      |
        |      |
        |

跑:

for (int i = 0; i < 10; i++) {
    SomeNode r1 = new SomeNode ();
    myPane.addElement(r1);                      
}

后:

 A      B      C
 |      |      |
 |      |      |
 |      |
 |
 |
 |
 |
 |
 |
 |
 |

正确

 A      B      C
 |      |      |
 |      |      |
 |      |      |
 |      |      |
 |      |      |
 |

|= 某个节点

4

2 回答 2

33

你需要做的是:

  1. 创建要放置在 VBoxes 中的节点。
  2. 将它们添加到某个场景(它可以只是一个新的虚拟场景,不附加到舞台,但具有与您的主场景相同的 CSS 规则)。
  3. 在每个节点(或虚拟场景根)上调用applyCss()和。layout()
  4. 测量每个节点的布局边界。
  5. 根据您的布局算法将节点添加到真实场景中的 VBox。

有关的

要了解如何测量节点的大小,请参阅以下答案:

背景

JavaFX 布局计算通过应用 CSS 和布局传递来工作。通常,这作为脉冲的一部分发生(内部 JavaFX 系统中的一种自动 60fps 滴答声,它检查场景中是否需要应用新的 css 或布局的脏对象)。在大多数情况下,您可以只指定您想要对场景进行的更改,并让自动脉冲布局机制处理当时的布局;这样做非常有效,因为这意味着脉冲之间的任何变化都是批量处理的,您不需要手动触发布局通道。但是,当您需要在布局发生之前实际获取事物的大小(如您的情况)时,您需要在尝试查询节点的高度和宽度范围之前手动触发 CSS 应用程序和布局传递。

文档

不幸的是,该方法的详细 Java 8u20 Javadocnode.applyCSS()目前已损坏,因此我将在此处重现 Javadoc 代码中的示例,以便您可以在上下文中查看推荐的用法。对于下一个 Java 功能版本 (8u40),损坏的 javadoc 已修复为RT-38330 的一部分 Node 类的几个方法缺少 Javadoc,因此在该版本中,您将能够在 JavaFX javadoc 中找到以下文本:

如果需要,将样式应用于此节点及其子节点(如果有)。此方法通常不需要直接调用,但可以与layout()在下一个脉冲之前或如果场景不在舞台中时调整节点大小一起使用。

如果 Node 的 Scene 不为 null,则 CSS 将应用于此 Node,无论此 Node 的 CSS 状态是否干净。CSS 样式从该节点的最顶层父节点应用,其 CSS 状态不是 clean,这可能会影响其他节点的样式。如果节点不在场景中,则此方法是无操作的。场景不必在舞台中。

此方法不调用该layout()方法。通常,调用者将使用以下操作序列。

parentNode.applyCss();
parentNode.layout();

作为一个更完整的示例,以下代码使用applyCss()layout()在舞台显示之前查找 Button 的宽度和高度。如果调用applyCss() 或调用layout()被注释掉,调用getWidth()getHeight() 将返回零(直到舞台显示后的某个时间)。

public void start(Stage stage) throws Exception {
   Group root = new Group();
   Scene scene = new Scene(root);

   Button button = new Button("Hello World");
   root.getChildren().add(button);

   root.applyCss();
   root.layout();

   double width = button.getWidth();
   double height = button.getHeight();

   System.out.println(width + ", " + height);

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

布局()与请求布局()

调用 layout() 将立即执行布局传递。

调用 requestLayout() 将在未来执行布局传递:

请求在渲染下一个场景之前执行布局传递。这是异步批处理的,每个“脉冲”或动画帧发生一次。

因此 requestLayout() 通知系统需要进行布局,但不会立即进行布局。

通常不需要直接调用requestLayout(),因为大多数时候JavaFX系统能够在内部判断场景的某个区域是脏的,需要在需要的时候自动进行布局。

选择

这里的另一个选项是覆盖layoutChildren()父节点的方法,即“在布局传递期间调用以在此父节点中布局子节点。”。在这样做时,可能还需要覆盖computePrefHeight()prefWidth 和 min & max height & width 的方法以及其他计算方法。使用这种方法的详细描述很复杂,超出了这个答案的范围。

于 2014-10-02T00:49:05.467 回答
1
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import java.io.Closeable;

public class Sizing  extends Application {

  @Override
  public void start(Stage stage) {
    var label = new Label();
    var labelHeight = label.getHeight();
    System.out.println(labelHeight);

    try (var s = new SizingSandbox(label)) {
      labelHeight = label.getHeight();
    }

    System.out.println(labelHeight);
  }

  public static void main(String[] args)  { launch(args); }
}


class SizingSandbox extends Group  implements Closeable {

  public SizingSandbox(Node... nodes) {
    new Scene(this);
    getChildren().addAll(nodes);
    layItOut();
  }

  @Override
  public void close() {
    try {
      getChildren().removeAll();
    } catch (Exception e) { }
  }

  private void layItOut() {
    applyCss();
    layout();
  }
}

// output:
// 0.0
// 17.0
于 2022-01-17T03:19:34.407 回答