Я нашел решение :-) Подход Фабиана вдохновил меня (спасибо !!) И мой старый друг Пит помог мне с отладкой (также спасибо !!)
Кажется, что у алгоритма расположения макета JavaFX есть проблема, когда resize () применяется к повернутым панелям (или даже узлам - я не пробовал):
Следуя идее Фабиана, я отладил метод layoutChildren () класса Pane. Я обнаружил, что перемещение после setRotate () является правильным и сохраняет центр дочерней панели, как и ожидалось. Но как только вызывается resize () (что делается из-за повторной подгонки повернутой дочерней панели к ее отцу и, кроме того, всегда, когда пользователь изменяет размеры окна), исходное вычисление становится неверным:
На рисунке выше изображена последовательность setRotate (90), resize () и relocate () зеленого цвета и то же самое для setRotate (270) синим цветом. Маленький сине-зеленый кружок изображает соответствующий источник вместе с его координатами в примере 1024x786.
Анализ
Похоже, что для вычисления положение панели resize () использует не высоту и ширину из BoundsInParent-Property (см. JavaFX-Docu of Node), а из getWidth () и getHeight (), которые, кажется, отражают BoundsInLocal. Как следствие, для поворотов на 90 ° или 270 ° высота и ширина кажутся взаимозаменяемыми. Следовательно, ошибка в расчете для нового источника составляет только половину разницы между шириной и высотой (delta = (width-height) / 2), когда resize () пытается снова центрировать дочернюю панель после изменения размера.
Решение
Перемещение (delta, -delta) необходимо применять после изменения размера панелей с поворотом = 90 или 270 градусов.
Структура моей реализации следует основной идее Фабиана: я создал макет RotatablePaneLayouter: Region, который просто перезаписывает метод layoutChildren (). В своем конструкторе он получает панель (в моем примере StackPane), которая может содержать любое количество дочерних элементов (в моем примере ImageView) и которую можно вращать.
Затем LayoutChildren () просто выполняет resize () и relocate () для дочерней панели, чтобы полностью разместить ее в RotateablePaneLayouter с учетом ориентации дочерней панели.
Помощник Layouter (RotateablePaneLayouter: Region)
public class RotatablePaneLayouter extends Region {
private Pane child;
public RotatablePaneLayouter(Pane child) {
getChildren().add(child);
this.child = child;
// make sure layout gets invalidated when the child orientation changes
child.rotateProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
requestLayout();
}
});
}
@Override
protected void layoutChildren() {
// set fit sizes:
//resize child to fit into RotatablePane and correct movement caused by resizing if necessary
if ((child.getRotate() == 90)||(child.getRotate() == 270)) {
//vertical
child.resize( getHeight(), getWidth() ); //exchange width and height
// and relocate to correct movement caused by resizing
double delta = (getWidth() - getHeight()) / 2;
child.relocate(delta,-delta);
} else {
//horizontal
child.resize( getWidth(), getHeight() ); //keep width and height
//with 0° or 180° resize does no movement to be corrected
child.relocate(0,0);
}
}
}
Чтобы использовать его: сначала поместите панель, которую нужно повернуть, в Layouter, а не устанавливайте панель напрямую.
Здесь код основной программы примера. Вы можете использовать пробел, чтобы повернуть дочернюю панель на 90, 180, 270 и снова на 0 градусов. Вы также можете изменить размер окна с помощью мыши. Layouter всегда удается правильно разместить повернутую панель.
Пример использования Layouter
public class RotationTest extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage primaryStage) {
//image in a StackPane to be rotated
final ImageView imageView = new ImageView("file:D:/Test_org.jpg");
imageView.setPreserveRatio(true);
StackPane stackPane = new StackPane(imageView); //a stackPane is used to center the image
stackPane.setStyle("-fx-background-color: black;");
imageView.fitWidthProperty().bind(stackPane.widthProperty());
imageView.fitHeightProperty().bind(stackPane.heightProperty());
//container for layouting rotated Panes
RotatablePaneLayouter root = new RotatablePaneLayouter(stackPane);
root.setStyle("-fx-background-color: blue;");
Scene scene = new Scene(root, 1024,768);
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event) {
if (event.getCode() == KeyCode.SPACE) {
//rotate additionally 90°
stackPane.setRotate((stackPane.getRotate() + 90) % 360);
}
}
});
primaryStage.setTitle("Rotation test");
primaryStage.setScene(scene);
primaryStage.show();
}
}
Для меня это похоже на обход ошибки javaFX в resize ().