6

次のテストを見ると、新しい円が追加されるだけでなく、すべての円が移動することがわかります。毎回起こるわけではありません。新しい子が既存の境界の外にある場合のみだと思います。しかし、円をどこに置いても、別の円を追加したときにグループとそのすべての子が移動しないようにするにはどうすればよいですか?

グループにスケールを設定しないと、すべてが移動しないことに注意してください。つまり、スケールの設定に関連しています。

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)というのは、円の位置だけで動作を確認するのに十分であり、何が起こるかをより簡単に確認できるからです。また、デバッグのために、グループのレイアウト境界にバインドされたペインに可視の四角形を追加しました。そこで何が起こるかを確認できます。

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から:

グループに適用される変換、効果、または状態は、そのグループのすべての子に適用されます。このような変換と効果は、このグループのレイアウト境界には含まれませんが、変換と効果がこのグループの子に直接設定されている場合、それらはこのグループのレイアウト境界に含まれます。

スケールをオンにして結果を見る:

ここに画像の説明を入力

グループの境界が内部よりも大きいことがわかります。これは、変換の適用方法によるものです。グループの子は変換されますが、グループの境界の計算では、スケーリングは考慮されません。したがって、グループは円の変換されていない境界の和集合が存在するペイン上にあり、円の変換が適用されます。

このステートメントと、スケーリングがオフになっている場合の結果を比較してください。

ここに画像の説明を入力

要約すると、これは設計によるものであり、異常な動作ではありません。なぜなら、グループは常に同じ大きさであり、それに応じて、変換されていない子の境界の結合がある場所に配置されるからです。

編集

ノードをその位置でスケーリングし、グループを動かしたくない場合は、グループの子を直接スケーリングすることをお勧めします。このスレッドの実装では、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

ここでの本当の問題は、グループのピボット ポイント (回転とスケーリングの相対ポイント) がグループのレイアウト境界に依存していることです。したがって、新しい子の追加 (または既存の子の位置、サイズ、または回転の変更) によってレイアウト境界が変更されると、グループのすべての変換の相対ポイントも変更されます。

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

範囲外の値を持つ信号を描画するのと同じ問題がありました。

グループの代わりにペインを使用します。絶対配置も使用しますが、境界の周りを移動しません。 ペイン JavaFx 8

于 2015-10-10T22:28:21.037 に答える