Adding borders to GridPane JavaFX

41,488

Solution 1

Don't use setGridLinesVisible(true): the documentation explicitly states this is for debug only.

Instead, place a pane in all the grid cells (even the empty ones), and style the pane so you see the borders. (This gives you the opportunity to control the borders very carefully, so you can avoid double borders, etc.) Then add the content to each pane. You can also register the mouse listeners with the pane, which means you don't have to do the ugly math to figure out which cell was clicked.

The recommended way to apply a border to any region is to use CSS and a "nested background" approach. In this approach, you draw two (or more) background fills on the region, with different insets, giving the appearance of a border. So for example:

-fx-background-fill: black, white ;
-fx-background-insets: 0, 1 ;

will first draw a black background with no insets, and then over that will draw a white background with insets of 1 pixel on all sides, giving the appearance of a black border of width 1 pixel. While this may seem counter-intuitive, the performance of this is (allegedly) better than specifying border directly. You can also specify a sequence of four values for the insets for each fill, which are interpreted as the insets on the top, right, bottom, and left, respectively. So

-fx-background-fill: black, white ;
-fx-background-insets: 0, 0 1 1 0 ;

has the effect of a black border on the right and bottom, etc.

I'm also not sure SubScene is what you really want, unless you are intending attaching different cameras to each cell. If you really need a subscene, make the fill transparent to avoid drawing over the edges of the cell. You could just add the Group directly to each cell (you could probably just add the circle, depending on exactly what you need...).

Something like:

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.RowConstraints;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class Game2 extends Application{
    @Override
    public void start(final Stage stage) throws Exception {
        int rows = 5;
        int columns = 5;

        stage.setTitle("Enjoy your game");

        GridPane grid = new GridPane();
        grid.getStyleClass().add("game-grid");

        for(int i = 0; i < columns; i++) {
            ColumnConstraints column = new ColumnConstraints(40);
            grid.getColumnConstraints().add(column);
        }

        for(int i = 0; i < rows; i++) {
            RowConstraints row = new RowConstraints(40);
            grid.getRowConstraints().add(row);
        }

        for (int i = 0; i < columns; i++) {
            for (int j = 0; j < rows; j++) {
                Pane pane = new Pane();
                pane.setOnMouseReleased(e -> {
                    pane.getChildren().add(Anims.getAtoms(1));
                });
                pane.getStyleClass().add("game-grid-cell");
                if (i == 0) {
                    pane.getStyleClass().add("first-column");
                }
                if (j == 0) {
                    pane.getStyleClass().add("first-row");
                }
                grid.add(pane, i, j);
            }
        }


        Scene scene = new Scene(grid, (columns * 40) + 100, (rows * 40) + 100, Color.WHITE);
        scene.getStylesheets().add("game.css");
        stage.setScene(scene);
        stage.show();
    }

    public static class Anims {

        public static Node getAtoms(final int number) {
            Circle circle = new Circle(20, 20f, 7);
            circle.setFill(Color.RED);
            Group group = new Group();
            group.getChildren().add(circle);
//            SubScene scene = new SubScene(group, 40, 40);
//            scene.setFill(Color.TRANSPARENT);
            return group;
        }
    }

    public static void main(final String[] arguments) {
        Application.launch(arguments);
    }
}

and the css:

.game-grid {
    -fx-background-color: white ;
    -fx-padding: 10 ;
}
.game-grid-cell {
    -fx-background-color: black, white ;
    -fx-background-insets: 0, 0 1 1 0 ;
}
.game-grid-cell.first-row {
    -fx-background-insets: 0, 1 1 1 0 ;
}
.game-grid-cell.first-column {
    -fx-background-insets: 0, 0 1 1 1 ;
}
.game-grid-cell.first-row.first-column {
    -fx-background-insets: 0, 1 ;
}

Solution 2

Simply add an H and V gap of one pixel width and let the grid pane's background color "shine" through:

.my-grid-pane {
    -fx-background-color: lightgray;
    -fx-vgap: 1;
    -fx-hgap: 1;
    -fx-padding: 1;
}

If the grid pane's background color spreads from outside more than one pixel (will happen if its parent is larger than itself), just wrap the grid in a Group!

Share:
41,488
Jeet Parekh
Author by

Jeet Parekh

Updated on June 11, 2020

Comments

  • Jeet Parekh
    Jeet Parekh about 4 years

    I am creating a board game in JavaFX using GridPane.

    There are 7 different animations which could be placed in each grid (cell) of the grid.

    Initially the grid looks like this

    empty gridpane

    I tested adding a simple circle to it before programming my animation insertions. And it looks like this

    gridpane with subscenes

    enter image description here

    The nodes added are SubScenes which include TimeLine animation. Each cell size is 40x40 and the SubScene size is also 40x40.

    The subscenes when added, get on top of the gridpane border lines and it doesn't look good.

    What can I do so that the nodes are added below the grid lines? i.e. the gridlines are on top of the nodes.

    If it is not possible with GridPane, is there anything else I can use?

    class which i execute for the game

    class Game {
        static GridPane grid;
        public void start(final Stage stage) throws Exception {
            int rows = 5;
            int columns = 5;
    
            stage.setTitle("Enjoy your game");
            grid = new GridPane();
            for(int i = 0; i < columns; i++) {
                ColumnConstraints column = new ColumnConstraints(40);
                grid.getColumnConstraints().add(column);
            }
    
            for(int i = 0; i < rows; i++) {
                RowConstraints row = new RowConstraints(40);
                grid.getRowConstraints().add(row);
            }
    
            grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
                public void handle(MouseEvent me) {
                    grid.add(Anims.getAnim(1), (int)((me.getSceneX() - (me.getSceneX() % 40)) / 40), (int)((me.getSceneY() - (me.getSceneY() % 40)) / 40)); //here the getAnim argument could be between 1-7
                }
            });
    
            grid.setStyle("-fx-background-color: white; -fx-grid-lines-visible: true");
            Scene scene = new Scene(grid, (columns * 40) + 100, (rows * 40) + 100, Color.WHITE);
            stage.setScene(scene);
            stage.show();
        }
    
        public static void main(final String[] arguments) {
            Application.launch(arguments);
        }
    }
    

    class which contains animations, here I am just creating a circle

    public class Anims {
    
        public static SubScene getAnim(final int number) throws Exception {
            Circle circle = new Circle(20, 20f, 7);
            circle.setFill(Color.RED);
            Group group = new Group();
            group.getChildren().add(circle);
            SubScene scene = new SubScene(group, 40, 40);
            scene.setFill(Color.WHITE);
            return scene;
        }
    }
    
  • Jeet Parekh
    Jeet Parekh over 8 years
    I am trying to make this compatible with my code, as there are a lot of things going on when a cell is clicked apart from getting an animation. I don't know much about css, so could you please explain in brief what those numbers are in the .css file?
  • jewelsea
    jewelsea over 8 years
    @vicky the numbers in the provided CSS are background insets for layered backgrounds, read up on them (and other CSS related topics) in the CSS reference guide.
  • Jeet Parekh
    Jeet Parekh over 8 years
    it works great, replaced SubScenes with Panes as you and jewelsea suggested, thanks
  • James_D
    James_D over 8 years
    Added an explanation for the "nested background" technique.
  • Mordechai
    Mordechai almost 7 years
    Nice, but wouldn't it be easier to add h and v gaps of 1 pixel and let the grid pane's background color "shine" through?
  • Oriel
    Oriel about 2 years
    wouldn't performance be better if gridPane draw lines instead of creating n*n panes and set border to each one?