如何在添加子项时不让JavaFX组移动?

men*_*ics 6 javafx-2

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

请注意,如果我没有在集团上设置比例,他们就不会全部移动.所以它与设置比例有关.

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;
}
Run Code Online (Sandbox Code Playgroud)

Seb*_*ian 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);
Run Code Online (Sandbox Code Playgroud)

那么,这里发生了什么?

来自集团的API:

应用于组的任何变换,效果或状态都将应用于该组的所有子项.此类转换和效果不会包含在此组的布局范围中,但是如果直接在此组的子项上设置转换和效果,则这些转换和效果将包含在此组的布局范围中.

在您的比例开启的情况下查看结果:

在此输入图像描述

你看到组的界限大于内部的界限.这是因为转换的应用方式:转换组的子项,但是为了计算组的边界,不考虑缩放.因此,该组位于窗格中,其中圆的未转换边界的并集,然后应用圆的变换.

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

在此输入图像描述

总而言之,这是设计而不是一种奇怪的行为,因为集团总是那么大,并且相应地定位在其未变换的儿童界限的结合处.

编辑

如果您希望节点在它们的位置缩放而组不移动,我建议直接缩放组的子节点.这个线程的实现每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();
Run Code Online (Sandbox Code Playgroud)


Ser*_*nev 1

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

您可以:

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

    pane.getChildren().add(new Circle(...
    
    Run Code Online (Sandbox Code Playgroud)
  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);
    
    Run Code Online (Sandbox Code Playgroud)