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://mcmap.net/q/541837/-javafx-update-imageview-on-gridpane-click */
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://mcmap.net/q/541837/-javafx-update-imageview-on-gridpane-click */
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);
}
}