6

如果您观看以下测试,您会看到所有圆圈都在移动,而不仅仅是添加了一个新圆圈。它不会每次都发生。我认为只有当新孩子超出现有范围时。但是,当我添加另一个圈子时,无论我把圈子放在哪里,我如何才能得到它,以便它不会移动组和它的所有子项?

请注意,如果我不在 Group 上设置比例,它们将不会全部移动。所以这与设置比例有关。

import javafx.application.*;
import javafx.beans.value.*;
import javafx.collections.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.shape.*;
import javafx.stage.*;

import java.util.*;


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

    public void start(Stage stage) {
        Pane pane = new Pane();

        Group root = new Group();
        // NOTE: removing these two setScale* lines stops the undesirable behavior
        root.setScaleX(.2); 
        root.setScaleY(.2);
        root.setTranslateX(100);
        root.setTranslateY(100);

        root.layoutXProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observableValue, Number number, Number number2) {
                System.out.println("root layout: " + root.getLayoutX() + ", " + root.getLayoutY());
            }
        });

        root.getChildren().addListener(new ListChangeListener<Node>() {
            @Override public void onChanged(Change<? extends Node> change) {
                System.out.println("root: " + root.getBoundsInParent());
                System.out.println("root: " + root.getBoundsInLocal());
                System.out.println("root: " + root.getLayoutBounds());
                System.out.println("root: " + root.getLayoutX() + ", " + root.getLayoutY());
            }
        });

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

        new Thread(() -> {
            Random r = new Random();
            try {
                while (true) {
                    expand = expand * 1.1;
                    Thread.sleep(700);
                    Platform.runLater(() -> {
                        root.getChildren().add(new Circle(r.nextInt((int)(1000*expand)) - 500*expand, r.nextInt((int)(1000*expand)) - 500*expand, r.nextInt(50)+30));
                    });
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }

    static double expand = 1.0;
}
4

5 回答 5

5

首先,我想说你看到的行为可以通过一个小得多的程序来实现,更不用说你为圆圈做的那些计算了。r.nextInt(250)因为圆圈的位置足以看到行为并且更容易看到发生了什么。此外,为了调试,我在绑定到 Group 的 layoutbounds 的窗格中添加了一个可见矩形,您可以在其中看到发生了什么:

final Rectangle background = new Rectangle(0, 0, 0, 0);

    root.layoutBoundsProperty().addListener(new ChangeListener<Bounds>() {
      @Override
      public void changed(ObservableValue<? extends Bounds> observable, Bounds oldValue, Bounds newValue) {
        background.setX(newValue.getMinX());
        background.setY(newValue.getMinY());
        background.setWidth(newValue.getWidth());
        background.setHeight(newValue.getHeight());
      }
    });
    background.setFill(null);
    background.setStroke(Color.RED);
    pane.getChildren().add(background);

那么,这里发生了什么?

来自Group的 API:

应用于组的任何变换、效果或状态都将应用于该组的所有子项。此类变换和效果不会包含在此 Group 的布局边界中,但是如果直接在此 Group 的子级上设置变换和效果,则它们将包含在此 Group 的布局边界中。

在打开秤的情况下查看结果:

在此处输入图像描述

您会看到该组的范围大于内部的范围。这是因为如何应用变换:组的子项被变换,但在计算组的边界时不考虑缩放。因此,该组位于圆的未变换边界的并集所在的窗格上,然后应用圆的变换。

将此语句与关闭缩放时的结果进行比较:

在此处输入图像描述

总而言之,这是设计使然,而不是奇怪的行为,因为Group 总是一样大,并且相应地定位在其未转换子边界的并集所在的位置。

编辑

如果您希望节点在它们所在的位置缩放并且组不移动,我建议直接缩放组的子级。您的线程的此实现每 5 个圆圈更改圆圈的缩放比例,但它们保持在相同的位置:

new Thread(new Runnable() {
      private int count = 0;
      private double scale1 = .5;
      private double scale2 = .2;
      private double currentScale = scale1;
      @Override
      public void run() {
        final Random r = new Random();
        try {
          while (true) {
            expand = expand * 1.1;
            Thread.sleep(700);
            Platform.runLater(new Runnable() {
              @Override
              public void run() {
                System.out.println(count);
                Circle c = new Circle(r.nextInt(250), r.nextInt(250), 30);
                c.setScaleX(currentScale);
                c.setScaleY(currentScale);
                root.getChildren().add(c);
                count++;
                if (count > 5){
                  count = 0;
                  if (currentScale == scale1){
                    currentScale = scale2;
                  } else {
                    currentScale = scale1;
                  }
                  Iterator<Node> iterator = root.getChildren().iterator();
                  while (iterator.hasNext()) {
                    Node next = iterator.next();
                    next.setScaleX(currentScale);
                    next.setScaleY(currentScale);
                  }
                }
              }
            });
          }
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }).start();
于 2013-07-03T07:46:35.613 回答
4

这里真正的问题是 Group 的枢轴点——旋转和缩放的相对点——取决于 Group 的布局边界。因此,如果 layout-bounds 发生变化,由于添加了新的子项(或现有的子项改变了位置、大小或旋转),Group 上所有转换的相对点也会发生变化。

在 JavaFX 源代码中,您可以在 Node.java 文件中找到枢轴点的定义。方法impl_getPivotX()impl_getPivotY()返回中心 x 。布局边界的 y 坐标。

不幸的是,没有手动设置枢轴点的选项。但是,由于每个节点都管理着一个应用在标准转换之上的附加转换列表,因此您可以轻松实现所需的行为:

final Scale scale = new Scale();
group.getTransforms().add(scale);

// change scale
scale.setX(2);
于 2013-09-13T08:52:53.423 回答
1

因为您在添加每个圆圈后更改组的大小,从而触发窗格内的组重定位。

您可以:

  1. 将圆圈直接添加到窗格

    pane.getChildren().add(new Circle(...
    
  2. 添加大背景以修复组大小:

    // numbers are huge because you are using 1/5 scaling
    Rectangle base = new Rectangle(-10000, -10000, 20000, 20000);
    base.setFill(Color.LIGHTGRAY);
    root.getChildren().add(base);
    
于 2013-07-02T15:30:03.697 回答
1

我在绘制一个具有一些超出范围的值的信号时遇到了同样的问题。

使用窗格而不是组。您也使用绝对定位,它不会围绕其边界移动。 窗格 JavaFx 8

于 2015-10-10T22:28:21.037 回答
-1

Use javafx.scene.layout.Pane instead of javafx.scene.Group.

于 2016-02-08T18:35:50.053 回答