0

I am experimenting with JavaFX animation. I am spawning rectangles at a random location on the screen, and they move with the ball that i defined. whenever a rectangle goes off the left of the screen, i delete it from the scene, and from the ArrayList that contains it. Here is my code:

package UI;

import javafx.animation.AnimationTimer;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;

import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.Random;

public class UIController extends AnimationTimer {

@FXML
private Label fpsLbl;

@FXML
private Circle moveMeCircle;

@FXML
private AnchorPane root;

private ArrayList<Rectangle> rectangles = new ArrayList<>();

int fps = 0;

int deltaX = 5;
int deltaY = 0;

long start = 0;

@FXML
public void initialize() {
    super.start();
}

@Override
public void handle(long l) {
    if (start == 0) {
        start = l;
    } else {
        if (l >= start + 1000000000) {
            fpsLbl.setText("FPS: " + ((l - start) / 1000000000) * fps);
            System.out.println(fps);
            renderShape();
            if (fps < 59) {
                System.out.println(rectangles.size());
            }
            fps = 0;
            start = l;
        }
    }
    updateElements();
    fps++;
}

private void renderShape() {
    try{
    for (Rectangle rectangle : rectangles) {
        if (rectangle.getX()+rectangle.getWidth() < 0) {//this is the part 
            rectangles.remove(rectangle);               //that causes the
            root.getChildren().remove(rectangle);       //exception

        }
    }}
    catch(ConcurrentModificationException e){
        e.printStackTrace();
        }
    Random rand = new Random();
    int posX = rand.nextInt(1280);
    int posY = rand.nextInt(720);
    rectangles.add(new Rectangle(posX, posY, 100, 50));


    for (Rectangle rect : rectangles) {
        if (!root.getChildren().contains(rect)) {
            rect.setWidth(rand.nextInt(300 + 650));
            root.getChildren().add(rect);
        }
    }
}

private void updateElements() {
    if ((moveMeCircle.getCenterX() >= 1280 && deltaX > 0) || (moveMeCircle.getCenterX() <= 0 && deltaX < 0)) {
        deltaX = deltaX * -1;
    }
    if ((moveMeCircle.getCenterY() <= 0 && deltaY < 0) || (moveMeCircle.getCenterY() >= 720 && deltaY > 0)) {
        deltaY = deltaY * -1;
    }
    for (Rectangle rect : rectangles) {
        rect.setX(rect.getX() + deltaX);
        rect.setY(rect.getY() + deltaY);
    }
    moveMeCircle.setCenterX(moveMeCircle.getCenterX() + deltaX);
    moveMeCircle.setCenterY(moveMeCircle.getCenterY() + deltaY);
}
}

Whilst it does throw an Exception, it still does visually "delete" the elements, so my question is, how can i avoid having this error occur, and is there a better method i can use to achieve my desired result?

Thanks.

Here is the full StackTrace:

java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at UI.UIController.renderShape(UIController.java:61)
at UI.UIController.handle(UIController.java:47)
at javafx.animation.AnimationTimer$AnimationTimerReceiver.lambda$handle$484(AnimationTimer.java:57)
at java.security.AccessController.doPrivileged(Native Method)
at javafx.animation.AnimationTimer$AnimationTimerReceiver.handle(AnimationTimer.java:56)
at com.sun.scenario.animation.AbstractMasterTimer.timePulseImpl(AbstractMasterTimer.java:357)
at com.sun.scenario.animation.AbstractMasterTimer$MainLoop.run(AbstractMasterTimer.java:267)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:506)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:490)
at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$404(QuantumToolkit.java:319)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)

and also the FXML file, although it is mostly irrelevant, i just define the Anchorpane, and the ball that is moving:

<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.shape.Circle?>
<?import javafx.scene.shape.Rectangle?>
<?import javafx.scene.control.Label?>
<AnchorPane fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" 
minHeight="-Infinity" minWidth="-Infinity" prefHeight="720.0"
        prefWidth="1280.0" scaleShape="false" style="-fx-background-color: 
        #a4a4bd;"stylesheets="@css/main.css" 
        xmlns="http://javafx.com/javafx/10" 
        xmlns:fx="http://javafx.com/fxml/1"fx:controller="UI.UIController">
    <children>
        <Label fx:id="fpsLbl" prefWidth="200" AnchorPane.leftAnchor="0" 
        style="-fx-text-fill: blueviolet; -fx-font:20px 'Agency FB'"/>
        <Circle radius="10" centerX="50" centerY="50" fx:id="moveMeCircle"/>
    </children>
</AnchorPane>
HamishD
  • 349
  • 2
  • 15
  • Same problem as other questions regarding this exception and removal from an ArrayList. Use an iterator to solve this. See the answers to similar questions for the details. – Hovercraft Full Of Eels May 29 '18 at 14:25
  • @HovercraftFullOfEels so it's the arraylist causing this? i had assumed it was because i was deleting from arraylist and the children of root at the same time. thanks – HamishD May 29 '18 at 14:27
  • If you delete from a list while you're iterating through it, the iterator gets confused (it can't easily keep track of which element is supposed to be the "current" one). – James_D May 29 '18 at 14:28
  • @James_D Ah i see, so i should implement an iterator rather than a for:each? – HamishD May 29 '18 at 14:29
  • 1
    See linked questions... – James_D May 29 '18 at 14:30

0 Answers0