【问题标题】:JavaFX: Update ImageView on GridPane clickJavaFX:在 GridPane 上单击更新 ImageView
【发布时间】:2021-11-24 11:47:30
【问题描述】:

我正在使用 JavaFX 开发扫雷游戏:

我的GridPane 是由ImageViews 组成的,我正试图找到一种方法来找出在GridPane 上单击的图像的列索引和行索引。我目前有这个代码:

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

});

但是,getColumnIndexgetRowIndex 方法正在返回 null。我做错了什么?

【问题讨论】:

  • 一个complicated solution using custom events。有比链接示例更简单的解决方案。它旨在演示创建和处理自定义事件,这不是解决此问题所必需的,但它确实提供了一种潜在的解决方案。
  • 可能为每个“单元”添加唯一的事件处理程序会更容易(“单元”是您放置在网格窗格中的节点)。这样您就“只知道”与哪个单元格进行了交互,因为只有与该特定单元格交互才会调用该特定事件处理程序。

标签: java javafx mouseevent gridpane


【解决方案1】:

从这个example 开始,下面的变体用N x NButton 实例填充GridPane,将每个实例添加到List<Button>getGridButton() 方法展示了如何根据其网格坐标有效地获取按钮引用,并且动作监听器显示点击和找到的按钮是相同的。

每个网格按钮如何知道自己在网格上的位置?每个人都有自己的anonymous class 实例——按钮的事件处理程序——可以访问传递给createGridButton() 的行和列参数。这些参数是effectively final;相比之下,使用早期 Java 版本的原始 example 必须将参数显式设为 final。

虽然此方法获取网格中任何Node 的行和列,但Button ToggleButton 在这种情况下可能更方便。特别是,您可以使用setGraphic​()ContentDisplay.GRAPHIC_ONLY 来获得所需的外观。

顺便说一句,GridPane 方法 getColumnIndexgetRowIndex 返回 null,因为它们引用了孩子的索引约束,如果设置,如图所示 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, row, col);
            GridPane.setMargin(gb, new Insets(N));
        }
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}

对于使用二维数组的模型,还可以考虑List&lt;List&lt;Button&gt;&gt;,如下图所示:

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, row, col);
                GridPane.setMargin(gb, new Insets(N));
            }
        }
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

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

【讨论】:

    【解决方案2】:

    +1 为所有 cmets 和@trashgod 提供的答案。

    但试图解决您的实际问题,我认为使用 event.getSource 是您问题的根本原因。从事件返回的源将始终是 GridPane。

    event.getTarget() 方法将为您提供您单击的目标节点。所以我认为将其更改为 getTarget() 将解决您的问题。

    下面是我的意思的工作演示。 即使我没有明确设置索引约束,这也有效

    更新:我误认为显式约束设置只能通过 GridPane.add 方法完成。但是使用 add/addRow/addColumn 方法也会设置索引约束。

    注意:如果您单击的目标节点是 GridPane 的直接子节点(在您的情况下),这将起作用。但是,如果您的目标节点不是 GridPane 的直接子节点,例如 StackPane 有一个标签并且如果您单击该标签,则会引发空异常。

    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);
        }
    }
    

    【讨论】:

    • 如果我理解正确,addRow() 会隐式设置约束;引用here
    • @trashgod,感谢您的更正:)。我通过使用 getChildren().add() 添加节点快速检查,它没有设置约束。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-16
    • 1970-01-01
    • 1970-01-01
    • 2014-12-11
    • 1970-01-01
    相关资源
    最近更新 更多