2012-04-22 188 views
0

我想顯示5個隨機定位和彩色圓圈。這很容易。現在我想將此代碼調整爲動畫。這個應用程序應該無限制地產生隨機的圓圈,但條件是它應該只保留屏幕上的最後五個圓圈。這是我卡住的地方。 JavaFx提供ListChangeListener。我認爲這是我應該使用的。但是如何? 以下是我未完成的代碼:
javafx動畫:顯示圓圈

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了。

回答

2

在代碼中,你有一些錯誤:

  • GUI中沒有顯示,因爲,執行流程永遠不會到達primaryStage.show();由於init(primaryStage);無限循環。
  • 新的ListChangeListener被一次又一次地加入循環中。但是,在正常情況下,只能添加一次。
  • 您正在操縱olol.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
此外,如果您確實需要真正的動畫,請參閱示例Colorful Circles Application

4

Oracle forums last year也提出了類似的問題。

這裏是使用時間線的示例解決方案,我更喜歡依靠工作線程的解決方案。儘管兩者都可以完成工作,但我發現使用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(); 
    } 
}