简体   繁体   English

javafx动画:显示圆圈

[英]javafx animation: displaying circles

I want to display 5 randomly positioned and colored circles. 我想显示5个随机定位的彩色圆圈。 It was easy part. 这很容易。 Now I want to adjust this code to be an animation. 现在,我想将此代码调整为动画。 This application should generate random circles endlessly but the condition is that it should keep only last five circles on the screen. 该应用程序应无休止地生成随机圆圈,但条件是它应仅在屏幕上保留最后五个圆圈。 This is where I got stuck. 这就是我卡住的地方。 JavaFx provides ListChangeListener. JavaFx提供ListChangeListener。 I think it is what I should use. 我认为这是我应该使用的。 But how? 但是如何? The following is my unfinished code: 以下是我未完成的代码:

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); }
}

After having managed to make ListChangeListener compile without errors it doesn't still work the way expected. 设法使ListChangeListener编译无误后,它仍无法按预期方式工作。 Changes made to for loop: 对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 loop is defined to loop infinitely (probably not the right way to solve this problem also) and I can see from console that circles are removed and added during the program run. For循环被定义为无限循环(也可能不是解决此问题的正确方法),我从控制台中可以看到在程序运行期间已删除并添加了圆圈。 Alas, I can't see GUI anymore. las,我再也看不到GUI。

A similar question was also asked on the on the Oracle forums last year . 去年Oracle论坛上也提出了类似的问题。

Here is sample solution using Timeline, which I prefer to a solution relying on worker threading. 这是使用时间轴的示例解决方案,我更喜欢依赖于工作者线程的解决方案。 Though both can get the job done, I find using the JavaFX animation APIs more elegant and less error prone. 尽管两者都可以完成工作,但是我发现使用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();
  }
}

In your code, you have some mistakes: 在您的代码中,您有一些错误:

  • the GUI is not shown because, the execution flow never reaches the primaryStage.show(); 未显示GUI,因为执行流永远不会到达primaryStage.show(); due to infinite loop in the init(primaryStage); 由于init(primaryStage);中的无限循环init(primaryStage); .
  • new ListChangeListener is added again and again in a loop. 新的ListChangeListener一次又一次地添加到循环中。 However you should add it only once in normal situations. 但是,在正常情况下,您只能添加一次。
  • You are manipulating the ol ( ol.remove(0); ) in its own listener which triggers the new change event recursively. 您正在其自己的侦听器中操作olol.remove(0); ),它以递归方式触发新的更改事件。

As a solution: periodic tasks, long-time background executions can be separated to a different thread. 作为解决方案:定期任务,长时间的后台执行可以分离到另一个线程。

    @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();
    }  

I have only changed the content of start(Stage primaryStage) . 我只更改了start(Stage primaryStage) There is no need to add a listener. 无需添加侦听器。 This solution is very quick but not elegant way. 该解决方案非常快速,但不是一种优雅的方法。 You must manage the thread yourself. 您必须自己管理线程。 For more elegant and modern approach refer to Worker Threading in JavaFX 2.0 . 有关更优雅,更现代的方法,请参阅JavaFX 2.0中的Worker Threading
In addition, if you really want a real animation then see the example Colorful Circles Application . 另外,如果您真的想要一个真实的动画,请参阅示例Colorful Circles Application

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM