2

下面的示例代码。这将为所有内容输出全零。对于 ToolBar,它也显示零。对于文本,它输出非零宽度和高度。这是标签上的错误,还是故意的?我应该做些什么来让 Label 有它的界限吗?我尝试过其他线程,移动,各种东西。

注意:所有节点都按预期显示。只是获取 Bounds 是行不通的。

我看到了一些关于可能在 boundsInParent 属性上使用侦听器的信息。当我尝试这样做时,数字会在许多事件中反弹,所以似乎没有办法确定我什么时候会有正确的值。

import javafx.application.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;


public class TestBounds extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    public void start(Stage stage) {
        Pane root = new Pane();
        Group g = new Group();
        root.getChildren().add(g);

        Scene scene = new Scene(root, 500, 500);
        stage.setScene(scene);
        stage.show();

        Platform.runLater(() -> {
//            Text text = new Text("test label");
//            ToolBar text = new ToolBar(new Button("test label"));
            Label text = new Label("test label");
            g.getChildren().add(text);
            System.out.println(text.getBoundsInParent());
            System.out.println(text.getBoundsInLocal());
            System.out.println(text.getLayoutBounds());
        });
    }
}

@sarcan 的回答是一个好的开始,但并不完整。这是一个例子:

package other;

import javafx.application.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;


public class Main extends Application {

    @Override
    public void start(Stage stage) {
        Pane root = new Pane();
        final Group g = new Group();
        root.getChildren().add(g);

        Scene scene = new Scene(root, 500, 500);
        stage.setScene(scene);
        stage.show();

        Platform.runLater(() -> {
            final Label text = new Label("test label");
            g.getChildren().add(text);
            new Thread(() -> {
                Platform.runLater(() -> {
                    System.out.println("text.getWidth() = " + text.getWidth());
                    System.out.println(text.getBoundsInParent());
                    System.out.println(text.getBoundsInLocal());
                    System.out.println(text.getLayoutBounds());
                });
            }).start();
        });
    }

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

您可以删除新的 Thread() 部分,它仍然会显示问题。我把它扔在那里只是为了表明我真的想按照建议“推迟”它。上面的示例按照推荐使用了 runLater,但仍然演示了该问题。边界全部输出为 0。

上面的例子很常见——当已经在 FX 线程中并且需要访问它的边界时添加一个节点。但是如何可靠地延迟,以便在场景更新后获得这些边界?Platform.runLater 显然没有这样做。

4

1 回答 1

7

这里的问题是您将文本添加到其父级,然后立即查询其坐标。JavaFX 和大多数其他工具包的工作方式与您预期的有点不同:添加标签时,场景图不会立即更新。相反,它被标记为“脏”。当 JavaFX 应用程序线程存在您的块时,它将执行更新,包括布置您的组并相应地设置标签的边界。

这样做的原因很简单。如果 JavaFX 会立即更新场景图,不仅浪费,还会导致奇怪的结果。假设您要添加 20 个标签,这会触发 20 个即时更新。由于这些会发生得非常快,因此用户界面会闪烁。相反,您添加了 20 个标签,这会导致 ui 被标记为脏 20 次(这非常便宜),然后您的所有更改都将在一次更新中应用。将您的 runLater 块视为“向 JavaFX 描述您希望 ui 在下一次原子更新后的样子”。

现在,话虽如此,如果您想获得文本的边界,只需先给 JavaFX 一个更新的机会。例如,如果您将添加的文本移动到 runLater 块前面,则会添加文本,更新场景图,然后您将查询坐标 - 这将产生正确的结果:

public void start(Stage stage) {
    Pane root = new Pane();
    final Group g = new Group();
    final Label text = new Label("test label");
    g.getChildren().add(text);
    root.getChildren().add(g);

    Scene scene = new Scene(root, 500, 500);
    stage.setScene(scene);
    stage.show();

    Platform.runLater(new Runnable() {
        public void run() {
            System.out.println("text.getWidth() = " + text.getWidth());
            System.out.println(text.getBoundsInParent());
            System.out.println(text.getBoundsInLocal());
            System.out.println(text.getLayoutBounds());
        }
    });
}
于 2013-06-30T09:15:09.550 回答