以下示例代码。这会为所有内容输出全零。对于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());
});
}
}
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显然不会这样做。
答案 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());
}
});
}