Home > other >  Setting mouse event to multiple labels in a loop
Setting mouse event to multiple labels in a loop

Time:04-30

I have a grid of 100 javafx labels in a program using a gui I made in scenebuilder. I'm trying to set it up so whenever you click on a label, the label turns the color green.

I have the labels in a two dimensional array list. I'm using a nested for loop to traverse the list, and then then using the setOnMouseClicked function to set the mouse event, instead of creating a function for all 100 labels.

However, I cannot access the for loop control variables in the EventHandler method.

for (int i = 0; i < 10; i  )
        {
            for (int j = 0; j < 10; j  )
            {
                labels.get(i).get(j).setOnMouseClicked(new EventHandler<MouseEvent>(){
                    
                    @Override
                    public void handle(MouseEvent event) {
                        
                        labels.get(i).get(j).setTextFill(Color.GREEN);
                        
                    }
                    
                });
            }
        }

Local variable i defined in an enclosing scope must be final or effectively final

The error only pops up for 'i' in ecllipse but I assume its affecting both.

What is a different way around this problem? I don't have to use a loop to set the mouse events up, that was just my attempt at solving the problem of setting up 100 labels to be clicked on.

CodePudding user response:

This is really a duplicate of:

  • image

    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.layout.TilePane;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import static javafx.scene.paint.Color.GREEN;
    
    public class MultiLabelApplication extends Application {
        private static final int N_ROWS = 5;
        private static final int N_COLS = 5;
    
        @Override
        public void start(Stage stage) {
            Label lastClicked = new Label("No Selection");
            lastClicked.setPadding(new Insets(10));
    
            TilePane grid = new TilePane(5, 5);
            grid.setPrefRows(N_ROWS);
            grid.setPrefColumns(N_COLS);
            grid.setMinSize(TilePane.USE_PREF_SIZE, TilePane.USE_PREF_SIZE);
            grid.setMaxSize(TilePane.USE_PREF_SIZE, TilePane.USE_PREF_SIZE);
    
            List<List<Label>> labels = new ArrayList<>(new ArrayList<>());
            for (int i = 0; i < N_ROWS; i  ) {
                final int row = i;
    
                labels.add(new ArrayList<>());
    
                for (int j = 0; j < N_COLS; j  ) {
                    final int col = j;
    
                    final Label label = new Label(row   ","   col);
                    label.setPadding(new Insets(10));
                    label.setOnMouseClicked(event -> {
                        label.setTextFill(GREEN);
                        lastClicked.setText(
                            "Last clicked row: "   row   ", col: "   col
                        );
                    });
    
                    labels.get(row).add(label);
                    grid.getChildren().add(label);
                }
            }
    
            VBox layout = new VBox(10, lastClicked, grid);
            layout.setPadding(new Insets(10));
    
            Scene scene = new Scene(layout);
            scene.getStylesheets().add(CSS);
    
            stage.setScene(scene);
            stage.setResizable(false);
    
            stage.show();
        }
    
        public static void main(String[] args) {
            launch();
        }
    
        public static final String CSS = "data:text/css,"   // language=CSS
                """
                .root {
                    -fx-font-size: 16px;
                    -fx-background-color: cornsilk;
                }
                
                .label {
                    -fx-background-color: palegreen; 
                    -fx-text-fill: darkgreen;
                }
                
                TilePane .label {
                    -fx-background-color: lightblue; 
                    -fx-text-fill: navy;
                }
                """;
    }
    

    CodePudding user response:

    Using @jewelsea's answer, I want to show you another way to do this without using final. I don't know if there are any drawbacks or pitfalls.

    You can use the MouseEvent to determine which node was pressed by calling event.getSource(). See the altered code below.

    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.layout.TilePane;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import static javafx.scene.paint.Color.GREEN;
    
    public class App extends Application {
        private static final int N_ROWS = 5;
        private static final int N_COLS = 5;
    
        @Override
        public void start(Stage stage) {
            Label lastClicked = new Label("No Selection");
            lastClicked.setStyle("-fx-background-color: palegreen; -fx-text-fill: darkgreen; -fx-font-size: 16px;");
            lastClicked.setPadding(new Insets(10));
    
            TilePane grid = new TilePane(5, 5);
            grid.setPrefRows(N_ROWS);
            grid.setPrefColumns(N_COLS);
            grid.setMinSize(TilePane.USE_PREF_SIZE, TilePane.USE_PREF_SIZE);
            grid.setMaxSize(TilePane.USE_PREF_SIZE, TilePane.USE_PREF_SIZE);
            grid.setStyle("-fx-background-color: cornsilk;");
    
            List<List<Label>> labels = new ArrayList<>(new ArrayList<>());
            for (int i = 0; i < N_ROWS; i  ) {
                final int row = i;
    
                labels.add(new ArrayList<>());
    
                for (int col = 0; col < N_COLS; col  ) {
                    Label label = new Label(row   ","   col);
                    label.setPadding(new Insets(10));
                    label.setStyle("-fx-background-color: lightblue; -fx-text-fill: navy; -fx-font-size: 16px;");
                    label.setOnMouseClicked(event -> {
                        Label tempLabel = ((Label)event.getSource());
                        tempLabel.setTextFill(GREEN);
                        lastClicked.setText("Last clicked row: "   tempLabel.getText());
                    });
    
                    labels.get(row).add(label);
                    grid.getChildren().add(label);
                }
            }
    
            VBox layout = new VBox(10, lastClicked, grid);
            layout.setPadding(new Insets(10));
    
            stage.setScene(new Scene(layout));
            stage.setResizable(false);
            stage.show();
        }
    
        public static void main(String[] args) {
            launch();
        }
    
    }
    

    enter image description here

    CodePudding user response:

    When you instantiate each EventHandler class, the loop variables i and j exist. However after the two loops have finished creating the listeners, the two variable are gone.

    What you need to do is pass into the listener the coordinates (i and j).

    for (int i = 0; i < 10; i  )
            {
                for (int j = 0; j < 10; j  )
                {
                    labels.get(i).get(j).setOnMouseClicked(new EventHandler<MouseEvent>(i, j){
    
    private int i;
    private int j;
    
    public EventHandler<MouseEvent>(int iIn, int jIn )
    {
    i = iIn;
    j = jIn;
    }
                        
                        @Override
                        public void handle(MouseEvent event) {
                            
                            labels.get(i).get(j).setTextFill(Color.GREEN);
                            
                        }
                        
                    });
                }
            }
    

    Um, I didn't actually test this, but I have passed in variables to listener classes before, though the classes were nested rather than inline.

  • Related