Если я вызываю Node.getBoundsInParent()
слишком рано после добавления узла в GridPane
, все члены возвращенного объекта Bounds
будут 0.0
. Вот код Java, демонстрирующий мою проблему:
import java.lang.Runnable;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
public class MyApplication extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
List<Label> labels = new ArrayList<Label>();
for (int index = 0; index < 12; ++index) {
labels.add(new Label(String.format("%d", index)));
}
GridPane gridPane = new GridPane();
Button layoutButton = new Button("Layout");
layoutButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
gridPane.getChildren().setAll(labels);
for (int index = 0; index < labels.size(); ++index) {
gridPane.setConstraints(labels.get(index), 0, index);
}
}
});
Button getBoundsButton = new Button("Get Bounds");
getBoundsButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println(labels.get(0).getBoundsInParent());
}
});
Button layoutAndGetBoundsButton = new Button("Layout and Get Bounds");
layoutAndGetBoundsButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
gridPane.getChildren().setAll(labels);
for (int index = 0; index < labels.size(); ++index) {
gridPane.setConstraints(labels.get(index), 0, index);
}
System.out.println(labels.get(0).getBoundsInParent());
}
});
Button layoutAndGetBoundsLaterButton = new Button("Layout and Get Bounds Later");
layoutAndGetBoundsLaterButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
gridPane.getChildren().setAll(labels);
for (int index = 0; index < labels.size(); ++index) {
gridPane.setConstraints(labels.get(index), 0, index);
}
class MyRunnable implements Runnable {
Label label;
public MyRunnable(Label label) {
this.label = label;
}
@Override
public void run() {
System.out.println(this.label.getBoundsInParent());
}
}
Platform.runLater(new MyRunnable(labels.get(0)));
}
});
VBox vbox = new VBox();
vbox.getChildren().setAll(
layoutButton,
getBoundsButton,
layoutAndGetBoundsButton,
layoutAndGetBoundsLaterButton);
HBox hbox = new HBox();
hbox.getChildren().setAll(vbox, gridPane);
primaryStage.setTitle("Race Condition?");
primaryStage.setScene(new Scene(hbox, 320, 240));
primaryStage.show();
}
}
И вот Jython, который впервые появился в моем вопросе:
import java.lang.Runnable as Runnable
import javafx.application.Application as Application
import javafx.application.Platform as Platform
import javafx.event.EventHandler as EventHandler
import sys
class MyApplication(Application):
@classmethod
def main(cls, args):
MyApplication.launch(cls, args)
def start(self, primaryStage):
import javafx.scene.Scene as Scene
import javafx.scene.control.Button as Button
import javafx.scene.control.Label as Label
import javafx.scene.layout.GridPane as GridPane
import javafx.scene.layout.HBox as HBox
import javafx.scene.layout.VBox as VBox
labels = [Label('%d' % index) for index in range(12)]
gridPane = GridPane()
layoutButton = Button('Layout')
def _handler(event):
gridPane.getChildren().setAll(labels)
for index, label in enumerate(labels):
gridPane.setConstraints(label, 0, index)
layoutButton.setOnAction(_handler)
getBoundsButton = Button('Get Bounds')
def _handler(event):
print labels[0].getBoundsInParent()
getBoundsButton.setOnAction(_handler)
layoutAndGetBoundsButton = Button('Layout and Get Bounds')
def _handler(event):
gridPane.getChildren().setAll(labels)
for index, label in enumerate(labels):
gridPane.setConstraints(label, 0, index)
print labels[0].getBoundsInParent()
layoutAndGetBoundsButton.setOnAction(_handler)
layoutAndGetBoundsLaterButton = Button('Layout and Get Bounds Later')
def _handler(event):
gridPane.getChildren().setAll(labels)
for index, label in enumerate(labels):
gridPane.setConstraints(label, 0, index)
class MyRunnable(Runnable):
def __init__(self, label):
self.label = label
def run(self):
print self.label.getBoundsInParent()
Platform.runLater(MyRunnable(labels[0]))
layoutAndGetBoundsLaterButton.setOnAction(_handler)
vbox = VBox()
vbox.getChildren().setAll([
layoutButton,
getBoundsButton,
layoutAndGetBoundsButton,
layoutAndGetBoundsLaterButton])
hbox = HBox()
hbox.getChildren().setAll([vbox, gridPane])
primaryStage.setTitle('Race Condition?')
primaryStage.setScene(Scene(hbox, 320, 240))
primaryStage.show()
MyApplication.main(sys.argv)
Если я последовательно нажимаю кнопки «Макет» и «Получить границы», моя программа, как и ожидалось, напечатает ограничивающую рамку с некоторыми ненулевыми элементами.
Если я нажму кнопку «Разметка и получение границ», моя программа напечатает ограничивающую рамку со всеми элементами, установленными в ноль. (Если щелкнуть эту кнопку в течение секунды, будут напечатаны ожидаемые результаты.)
Если я нажму кнопку «Разметить и получить границы позже», результаты будут не лучше.
Кстати, я вижу эту проблему с GridPane
прямо сейчас, но я видел аналогичные проблемы с другими элементами управления JavaFX: я прошу его сделать что-то, а затем прошу его сделать что-то еще, но он не совсем готов к сделай еще второе.
Что я делаю неправильно?
Обновление: похоже, я столкнулся с этой проблемой который был закрыт как «не проблема».