JavaFX - Event Handlers on different objects created by for loop

1.5k Views Asked by At

I want to create a set of ten different circles with a for loop and have each of them change color when the mouse cursor hovers over one of them and also have them change to a third color with a mouse click. However only one of the circles - the last one to be created in the loop- has the color changes, regardless which circle gets clicked or hovered over. Can anyone explain me why and how can I fix this? I would be very greatful. Hier is is my code:

public class View extends Parent{
BorderPane gameScreen;
Group hexaBlock;
ArrayList<Circle> circleList = new ArrayList<>(); 
Circle circle;
...
public View(){
        gameScreen = new BorderPane();
        hexaBlock = new Group(); 
        ...
        for(int y=0; y<2; y++ ){
            for(double x=0; x<5; x++){
                circle = new Circle(xPosition(hexagon width*x), yPosition(hexagon height*4*y), radius);
                circleList.add(circle);
                circle.setFill(Color.BLACK);
                circle.setOnMousePressed(new EventHandler<MouseEvent>() {
                    @Override
                    public void handle(MouseEvent event) {
                        circle.setFill(Color.CYAN);
                    }
                });
                circle.addEventHandler(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>(){
                    @Override
                    public void handle(MouseEvent t) {
                        circle.setFill(Color.RED);
                    }
                });
                circle.addEventHandler(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>(){
                     @Override
                        public void handle(MouseEvent t) {
                         circle.setFill(Color.BLACK);
                        }
                });
}
this.getChildren().add(gameScreen);
... 
gameScreen.setCenter(hexaBlock);
...
hexaBlock.getChildren().addAll(circleList);

.....

enter image description here

3

There are 3 best solutions below

0
On

circle is a field. When the event handlers are run, the field's value is retrieved and in this case it contains the value last assigned to it, i.e. the circle created last.

Note that you can access final (effectively final for java >= 8) local variables in surrounding scopes from anonymus classes. I recommend removing the circle field and declaring circle where you assign the value to it:

for(int y=0; y<2; y++ ){
    for(double x=0; x<5; x++){
        final Circle circle = new Circle(xPosition(hexagon width*x), yPosition(hexagon height*4*y), radius);

        ...
        // init circle handlers/properties
    }
}
2
On

This is my take on it, uncomment the setOnMouseExited if you want it to turn black again.

import java.util.Random;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class CircleColours extends Application {

    private final Random random = new Random();

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        AnchorPane pane = new AnchorPane();
        Scene scene = new Scene(pane, 600, 400);
        addCircles(pane, 10, 50);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public void addCircles(AnchorPane pane, int amount, int radius) {
        for (int i = 0; i < amount; i++) {
            Circle circle = new Circle(random.nextInt((int) pane.getWidth()), random.nextInt((int) pane.getHeight()), radius);
            circle.setOnMouseEntered(event -> circle.setFill(Color.rgb(random.nextInt(255), random.nextInt(255), random.nextInt(255))));
            circle.setOnMouseClicked(event -> circle.setFill(Color.rgb(random.nextInt(255), random.nextInt(255), random.nextInt(255))));
            //circle.setOnMouseExited(event -> circle.setFill(Color.BLACK));
            pane.getChildren().add(circle);
        }
    }

}
0
On

Here is a sample app. This app uses lambdas for the listeners.

import java.util.Random;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

/**
 *
 * @author blj0011
 */
public class JavaFXApplication54 extends Application
{

    @Override
    public void start(Stage primaryStage)
    {
        Random random = new Random();

        AnchorPane root = new AnchorPane();

        for (int i = 0; i < 5; i++)
        {
            int x1 = random.nextInt(300);
            System.out.println("l: " + x1);
            int y1 = random.nextInt(250);
            int radius = random.nextInt(10) + 3;
            root.getChildren().add(getCircle(x1, y1, radius));
        }

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        launch(args);
    }

    Circle getCircle(int x1, int y1, int radius)
    {
        Circle tempCircle = new Circle(x1, y1, radius);
        tempCircle.setFill(Color.BLACK);

        tempCircle.setOnMousePressed(me -> tempCircle.setFill(Color.CYAN));
        tempCircle.setOnMouseEntered(me -> tempCircle.setFill(Color.RED));
        tempCircle.setOnMouseExited(me -> tempCircle.setFill(Color.BLACK));

        return tempCircle;
    }
}