Edward Lim
Edward Lim

Reputation: 823

Accessing the column and row index of GridPane in JavaFX keeps returning null

Hi so I have tried many solutions online on how to get the Node(cell) that was clicked on in the Gridpane. The method I went with for getting the node is shown below. I have also tried iterating through all the nodes prior to this method. I have built the gridpane with scenebuilder and as you can see in the picture below the columns and row are clearly there. I am trying this solution How to get GridPane Row and Column IDs on Mouse Entered in each cell of Grid in JavaFX? however as I've mentioned above the values returned from the rows and columns are null.

@FXML
    private void mouseEntered(MouseEvent e) {
        Node source = (Node)e.getSource() ;
        System.out.println(source);
        Integer colIndex = userSelectionGrid.getColumnIndex(source);
        Integer rowIndex = userSelectionGrid.getRowIndex(source);
        if (colIndex == null && rowIndex == null) System.out.println("BOO");
        else
        System.out.printf("Mouse entered cell [%d, %d]%n", colIndex.intValue(), rowIndex.intValue());
    }

enter image description here

FXML containing this grid

<GridPane fx:id="userSelectionGrid" onMouseClicked="#mouseEntered" prefHeight="44.0" prefWidth="563.0">
         <columnConstraints>
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
         </columnConstraints>
         <rowConstraints>
            <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
         </rowConstraints>
         <children>
            <Label text="SAVE" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="0">
               <font>
                  <Font name="System Bold" size="13.0" />
               </font>
            </Label>
            <Label text="CANCEL" GridPane.columnIndex="0" GridPane.halignment="CENTER" GridPane.rowIndex="0">
               <font>
                  <Font name="System Bold" size="13.0" />
               </font>
            </Label>
            <Separator orientation="VERTICAL" GridPane.columnIndex="1" />
         </children>
         <VBox.margin>
            <Insets />
         </VBox.margin>
      </GridPane>

Upvotes: 0

Views: 2656

Answers (1)

fabian
fabian

Reputation: 82461

The source of the event is the Node where the handler is added, i.e. in this case the GridPane. You could add the handlers to the children of the GridPane for the source Node to be correct:

<GridPane fx:id="userSelectionGrid" prefHeight="44.0" prefWidth="563.0">
     <columnConstraints>
        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
     </columnConstraints>
     <rowConstraints>
        <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
     </rowConstraints>
     <children>
        <Label text="SAVE" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="0"  onMouseClicked="#mouseEntered">
           <font>
              <Font name="System Bold" size="13.0" />
           </font>
        </Label>
        <Label text="CANCEL" GridPane.columnIndex="0" GridPane.halignment="CENTER" GridPane.rowIndex="0"  onMouseClicked="#mouseEntered">
           <font>
              <Font name="System Bold" size="13.0" />
           </font>
        </Label>
        <Separator orientation="VERTICAL" GridPane.columnIndex="1" />
     </children>
     <VBox.margin>
        <Insets />
     </VBox.margin>
</GridPane>

Note that you could also make this work with your original fxml. However there are a few things that you should consider:

  1. null is also a valid return value for GridPane.getRowIndex and GridPane.getColumnIndex this doesn't mean the node is not the child of a GridPane, it just means the value has not been changed from the default. This has the same effect as using the value 0.
  2. The condition

    if (colIndex == null && rowIndex == null)
    

    Is too strong to prevent a NullPointerException in the else part. This could occur e.g. if the Separator is clicked.

  3. The target of the event is the node that is actually clicked, which could be a Node in the Label's skin. From this node you could traverse to the parent node until the parent is the GridPane to find the correct child of the GridPane.
@FXML
private void mouseEntered(MouseEvent e) {
    Node target = (Node) e.getTarget();
    // traverse towards root until userSelectionGrid is the parent node
    if (target != userSelectionGrid) {
        Node parent;
        while ((parent = target.getParent()) != userSelectionGrid) {
            target = parent;
        }
    }
    Integer colIndex = userSelectionGrid.getColumnIndex(target);
    Integer rowIndex = userSelectionGrid.getRowIndex(target);
    if (colIndex == null || rowIndex == null) {
        System.out.println("BOO");
    } else {
        System.out.printf("Mouse entered cell [%d, %d]%n", colIndex.intValue(), rowIndex.intValue());
    }
}

Note that the Labels do not fill the full GridPane unless you set the preferred size larger than the GridPane constraints. This messes up the layout of the GridPane. A workaround would be placing Regions behind the Labels in the cells. Those can be resized with the cells without influence on the GridPane layout, which allows you to catch the event on those nodes:

<GridPane fx:id="userSelectionGrid" prefHeight="44.0" prefWidth="563.0" onMouseClicked="#mouseEntered">                 
    <columnConstraints>
        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" fillWidth="true" />
        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" fillWidth="true" />
    </columnConstraints>
    <rowConstraints>
        <RowConstraints minHeight="10.0" vgrow="SOMETIMES" fillHeight="true" />
    </rowConstraints>
    <children>
        <Region GridPane.columnIndex="1" GridPane.rowIndex="0"/>
        <Region GridPane.columnIndex="0" GridPane.rowIndex="0"/>
        <Label text="SAVE" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="0">
            <font>
                <Font name="System Bold" size="13.0" />
            </font>
        </Label>
        <Label text="CANCEL" GridPane.columnIndex="0" GridPane.halignment="CENTER" GridPane.rowIndex="0">
            <font>
                <Font name="System Bold" size="13.0" />
            </font>
        </Label>
        <Separator orientation="VERTICAL" GridPane.columnIndex="1" />
    </children>
    <VBox.margin>
        <Insets />
    </VBox.margin>
</GridPane>

Upvotes: 2

Related Questions