如何可靠地获取Bounds JavaFX Label或ToolBar?

时间:2013-06-29 15:17:46

标签: javafx-2

以下示例代码。这会为所有内容输出全零。对于ToolBar,它还显示零。对于Text,它输出非零宽度和高度。这是Label上的错误,还是故意的?我应该做些什么来让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显然不会这样做。

1 个答案:

答案 0 :(得分:5)

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

这样做的原因很简单。如果JavaFX会立即更新场景图,那不仅会浪费,而且会导致奇怪的结果。假设您要添加20个标签,这会触发20个即时更新。因为那些会很快发生,ui会闪烁。相反,会发生什么是添加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());
        }
    });
}