0

ランダムに配置された色付きの円を 5 つ表示したいと考えています。簡単な部分でした。次に、このコードをアニメーションになるように調整します。このアプリケーションは、無限にランダムな円を生成する必要がありますが、条件は、画面上に最後の 5 つの円のみを保持することです。ここで行き詰まりました。JavaFx は ListChangeListener を提供します。使うべきものだと思います。しかし、どのように?以下は私の未完成のコードです:

import java.util.Random;

import javafx.application.Application;
import javafx.collections.ListChangeListener;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class RandomColorTest extends Application {

    int radius = 20;
    int sceneWidth = 300;
    int sceneHeight = 300;

    private void init(Stage primaryStage) {
        Group root = new Group();
        primaryStage.setResizable(false);
        primaryStage.setScene(new Scene(root, sceneWidth,sceneHeight));

        for (int i = root.getChildren().size(); i < 5; i++) {
            root.getChildren().add(createCircle());
            // the following should convey the idea:
            // if the collection holds 5 elements then keep least recently generated element for 1 second and then delete it
            // add one new element 
            // if the collection holds 5 elements then keep least recently generated element for 1 second and then delete it
            // add one new element 
            // and so on
            root.getChildren().addListener(new ListChangeListener<E>() {
                @Override
                public void onChanged(
                        javafx.collections.ListChangeListener.Change<? extends E> arg0) {
                    // TODO Auto-generated method stub
                }
            });

        }
    }

    // Create randomly positioned and colored circle
    private Circle createCircle() {
        final Circle circle = new Circle();
        circle.setRadius(radius);

        Random r = new Random();
        int rCol1 = r.nextInt(256);
        int rCol2 = r.nextInt(256);
        int rCol3 = r.nextInt(256);
        int rX = radius+r.nextInt(sceneWidth);
        if (rX>sceneWidth-radius) {
            rX=rX-2*radius;
        }
        int rY = radius+r.nextInt(sceneHeight);
        if (rY>sceneHeight-radius) {
            rY=rY-2*radius;
        }
        circle.setLayoutX(rX);
        circle.setLayoutY(rY);

        circle.setStroke(Color.BLACK);
        circle.setFill(Color.rgb(rCol1,rCol2,rCol3));
        System.out.println(rCol1+"-"+rCol2+"-"+rCol3+"-"+rX+"-"+rY);
        return circle;
    }

    @Override public void start(Stage primaryStage) throws Exception {
        init(primaryStage);
        primaryStage.show();
    }
    public static void main(String[] args) { launch(args); }
}

ListChangeListener をエラーなしでコンパイルできた後でも、期待どおりに動作しません。for ループに加えられた変更:

for (int i = root.getChildren().size();;i++) {
            final ObservableList<Node> ol = root.getChildren();
            // the following should convey the idea:
            // if the collection holds 5 elements then keep least recently generated element for 1 second and then delete it
            // add one new element 
            // if the collection holds 5 elements then keep least recently generated element for 1 second and then delete it
            // add one new element 
            // and so on
            ol.add(createCircle());
            ol.addListener( new ListChangeListener<Node>(){
                @Override
                public void onChanged(
                    javafx.collections.ListChangeListener.Change<? extends Node> arg0) {
                    // TODO Auto-generated method stub
                    System.out.println("one new element added, size:"+ol.size());
                    if (ol.size()==5) {
                        ol.remove(0);
                    }
                }   
            });
        } 

for ループは無限にループするように定義されています (おそらく、この問題を解決する正しい方法でもありません)。コンソールから、プログラムの実行中に円が削除されたり追加されたりすることがわかります。残念ながら、GUI が表示されなくなりました。

4

2 に答える 2

5

昨年の Oracle フォーラムでも同様の質問がありました。

これはタイムラインを使用したサンプル ソリューションです。これは、ワーカー スレッドに依存するソリューションよりも私が好むものです。どちらも仕事を成し遂げることができますが、JavaFX アニメーション API を使用する方がより洗練されており、エラーが発生しにくいことがわかります。

import javafx.animation.*;
import javafx.application.Application;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.Random;

public class FiveAutoCircleExample extends Application {
  private static final Random r = new Random();
  public static final int SCENE_SIZE = 800;

  public static void main(String[] args) throws Exception { launch(args); }
  public void start(final Stage stage) throws Exception {
    final Group circles = new Group();
    final Timeline animation = new Timeline(
      new KeyFrame(Duration.seconds(.5), 
      new EventHandler<ActionEvent>() {
        @Override public void handle(ActionEvent actionEvent) {
          while (circles.getChildren().size() >= 5) circles.getChildren().remove(0);
          int radius = 10 * r.nextInt(20);
          circles.getChildren().add(
            new Circle(
              r.nextInt(SCENE_SIZE - radius * 2) + radius, r.nextInt(SCENE_SIZE - radius * 2) + radius,
              radius,
              new Color(r.nextDouble(), r.nextDouble(), r.nextDouble(), r.nextDouble())
            )
          );
        }
      })
    );
    animation.setCycleCount(Animation.INDEFINITE);
    animation.play();

    // display the scene.
    stage.setScene(new Scene(circles, SCENE_SIZE, SCENE_SIZE, Color.CORNSILK));
    stage.show();
  }
}
于 2012-04-23T22:59:39.483 に答える
2

あなたのコードには、いくつかの間違いがあります:

  • primaryStage.show();の無限ループにより、実行フローが に到達しないため、GUI は表示されませんinit(primaryStage);
  • newListChangeListenerはループで何度も追加されます。ただし、通常は一度だけ追加する必要があります。
  • 新しい変更イベントを再帰的にトリガーする独自のリスナーで ol( )を操作しています。ol.remove(0);

解決策として、定期的なタスク、長時間のバックグラウンド実行を別のスレッドに分離できます。

    @Override
    public void start(Stage primaryStage) throws Exception {
        Group root = new Group();
        primaryStage.setResizable(false);
        primaryStage.setScene(new Scene(root, sceneWidth, sceneHeight));

        final ObservableList<Node> ol = root.getChildren();

        new Thread(new Runnable() {
            @Override public void run() {
                while (true) {
                    try {
                        // Wait for 2 seconds.
                        Thread.sleep(2000);
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                    Platform.runLater(new Runnable() {
                        @Override public void run() {
                            System.out.println("ol size:" + ol.size());
                            if (ol.size() == 5) {
                                ol.remove(0);
                            }
                            ol.add(createCircle());
                        }
                    });
                }
            }
        }).start();
        primaryStage.show();
    }  

の内容を変更しただけですstart(Stage primaryStage)。リスナーを追加する必要はありません。このソリューションは非常に迅速ですが、エレガントな方法ではありません。スレッドは自分で管理する必要があります。より洗練された最新のアプローチについては、Worker Threading in JavaFX 2.0を参照してください。
さらに、実際のアニメーションが本当に必要な場合は、カラフルなサークル アプリケーションの例を参照してください。

于 2012-04-23T12:33:35.020 に答える