Reputation: 21
I'm working on a Minesweeper game using JavaFX:
My GridPane
is made of ImageViews
, and I'm trying to find a way to figure out the column and row indexes of the image that was clicked on the GridPane
. I have this code currently:
gameGrid.setOnMouseClicked(event -> //gameGrid is my GridPane
{
Node source = (Node)event.getSource();
int columnIndex = GridPane.getColumnIndex(source);
System.out.println(columnIndex);
int rowIndex = GridPane.getRowIndex(source);
if(event.getButton()== MouseButton.PRIMARY)
game.newRevealCell(columnIndex,rowIndex);
else if(event.getButton()==MouseButton.SECONDARY)
game.getGrid()[columnIndex][rowIndex].setFlagged(true);
//game.getGrid is a 2D array containing the game cells
});
However, the methods getColumnIndex
and getRowIndex
are returning null
. What am I doing wrong?
Upvotes: 2
Views: 1404
Reputation: 205785
Starting from this example, the variation below fills a GridPane
with N
x N
instances of Button
, adding each to a List<Button>
. The method getGridButton()
shows how to obtain a button reference efficiently based on its grid coordinates, and the action listener shows that the clicked and found buttons are identical.
How does each grid button know its own location on the grid? Each one gets its own instance of an anonymous class—the button's event handler—that has access to the row and column parameters passed to createGridButton()
. These parameters are effectively final; in contrast, the original example using an earlier version of Java had to make the parameters explicitly final.
While this approach obtains the row and column of any Node
in the grid, a Button
or ToggleButton
may be more convenient in this context. In particular, you can use setGraphic()
with ContentDisplay.GRAPHIC_ONLY
to get the desired appearance.
As an aside, the GridPane
methods getColumnIndex
and getRowIndex
return null
as they refer to the child's index constraints, only if set, as illustrated here.
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
/** @see https://stackoverflow.com/a/69429741/230513 */
public class GridButtonTest extends Application {
private static final int N = 5;
private final List<Button> list = new ArrayList<>();
private Button getGridButton(int r, int c) {
int index = r * N + c;
return list.get(index);
}
private Button createGridButton(int row, int col) {
Button button = new Button("r" + row + ",c" + col);
button.setOnAction((ActionEvent event) -> {
System.out.println(event.getSource() == getGridButton(row, col));
});
return button;
}
@Override
public void start(Stage stage) {
stage.setTitle("GridButtonTest");
GridPane root = new GridPane();
for (int i = 0; i < N * N; i++) {
int row = i / N;
int col = i % N;
Button gb = createGridButton(row, col);
list.add(gb);
root.add(gb, col, row);
GridPane.setMargin(gb, new Insets(N));
}
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
For models using a two dimensional array, also consider a List<List<Button>>
, illustrated below:
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
/** @see https://stackoverflow.com/a/69429741/230513 */
public class GridButtonTest extends Application {
private static final int N = 5;
private final List<List<Button>> list = new ArrayList<>();
private Button getGridButton(int r, int c) {
return list.get(r).get(c);
}
private Button createGridButton(int row, int col) {
Button button = new Button("r" + row + ",c" + col);
button.setOnAction((ActionEvent event) -> {
System.out.println(event.getSource() == getGridButton(row, col));
});
return button;
}
@Override
public void start(Stage stage) {
stage.setTitle("GridButtonTest");
GridPane root = new GridPane();
for (int row = 0; row < N; row++) {
list.add(new ArrayList<>());
for (int col = 0; col < N; col++) {
Button gb = createGridButton(row, col);
list.get(row).add(gb);
root.add(gb, col, row);
GridPane.setMargin(gb, new Insets(N));
}
}
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Upvotes: 3
Reputation: 9959
+1 for all the comments and the answer provided by @trashgod.
But trying to address your actual issue, I think using event.getSource is the root cause for your issue. The source returned from the event will be always the GridPane.
event.getTarget() method will give you the target node you clicked. So I think changing it to getTarget() will fix your issue.
Below is the working demo of what I mean. This works even if I dont set the index contraint explicitly.
Update: I was mistaken that explicit constraint settings is done only through GridPane.add method. But using add/addRow/addColumn methods sets the index constraints as well.
Note: This will work if the target node you clicked is a direct child of GridPane (in your case). But if your target node is not a direct child of GridPane, like if the StackPane has a Label and if you clicked on that Label, a null exception will be thrown.
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class GridPaneIndexingDemo extends Application {
@Override
public void start(Stage stage) throws Exception {
StackPane root = new StackPane();
Scene scene = new Scene(root, 450, 450);
stage.setScene(scene);
stage.setTitle("GridPane");
stage.show();
GridPane grid = new GridPane();
grid.setGridLinesVisible(true);
grid.addRow(0, getPane(1), getPane(2), getPane(3));
grid.addRow(1, getPane(4), getPane(5), getPane(6));
grid.addRow(2, getPane(7), getPane(8), getPane(9));
grid.setOnMouseClicked(event -> {
Node source = (Node) event.getTarget();
int columnIndex = GridPane.getColumnIndex(source);
int rowIndex = GridPane.getRowIndex(source);
System.out.println("Row : " + rowIndex + ", Col : " + columnIndex);
});
root.getChildren().add(grid);
}
private StackPane getPane(int i) {
String[] colors = {"grey", "yellow", "blue", "pink", "brown", "white", "silver", "orange", "lightblue", "grey"};
StackPane pane = new StackPane();
pane.setPrefSize(150,150);
pane.setStyle("-fx-background-color:" + colors[i] + ";-fx-border-width:2px;");
return pane;
}
public static void main(String[] args) {
Application.launch(args);
}
}
Upvotes: 3