2

I'd like to create a grid as a background for my JavaFX application. My current solution is to paint a rectangle on a canvas, create an image pattern from it and set it as fill.

Question: Is there a better way to approach this, preferrably via CSS?

Current version:

public class BackgroundGrid extends Application {

    double gridSize = 20;

    @Override
    public void start(Stage primaryStage) {

        Scene scene = new Scene(new Group(), 800, 600);
        primaryStage.setScene(scene);
        primaryStage.show();

        scene.setFill(createGridPattern());

    }

    public ImagePattern createGridPattern() {

        double w = gridSize;
        double h = gridSize;

        Canvas canvas = new Canvas(w, h);
        GraphicsContext gc = canvas.getGraphicsContext2D();

        gc.setStroke(Color.BLACK);
        gc.setFill(Color.LIGHTGRAY.deriveColor(1, 1, 1, 0.2));
        gc.fillRect(0, 0, w, h);
        gc.strokeRect(0, 0, w, h);

        Image image = canvas.snapshot(new SnapshotParameters(), null);
        ImagePattern pattern = new ImagePattern(image, 0, 0, w, h, false);

        return pattern;

    }

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

enter image description here

Thank you very much!

Edit: in order to get sharp grid lines, just use

gc.strokeRect(0.5, 0.5, w, h);

I think that wouldn't be doable in CSS, isn't it?

enter image description here

Roland
  • 18,114
  • 12
  • 62
  • 93
  • 1
    See my blog post here: http://eckig.github.io/blog/2014/11/06/resizable-grid-using-canvas/ – eckig Mar 05 '15 at 05:46
  • Thanks, I've already seen that. I meant if it were possible to do everything with only css, not how to apply custom css. By the way, what do you think is faster? The solution with the pattern or the one with the drawing of the lines? I'm not sure since I don't know what happens internally. – Roland Mar 05 '15 at 06:23
  • I do not know which one is faster, but If I had to guess, I would say it depends on how much you resize the grid. – eckig Mar 05 '15 at 07:19

3 Answers3

5

You can do it with CSS too. This is all you need:

.root {
    -fx-background-color: #D3D3D333,
        linear-gradient(from 0.5px 0.0px to 10.5px  0.0px, repeat, black 5%, transparent 5%),
        linear-gradient(from 0.0px 0.5px to  0.0px 10.5px, repeat, black 5%, transparent 5%);
}

gradient

The 0.5px offset solves some buggy behavior when set from 0px to 10px, and some lines are rendered with two pixels instead of one:

wrong gradient

silvalli
  • 295
  • 2
  • 13
José Pereda
  • 44,311
  • 7
  • 104
  • 132
  • Looks ok without 0.5 offsets on Java 13 / openjfx 13 / macOS 10.14.6. – silvalli Nov 26 '19 at 20:38
  • Also, removing the 0.5 offsets allows the grid lines to be coincident with Line shape objects drawn along the grid lines on whole number coordinates. – silvalli Nov 27 '19 at 00:30
  • This css trick works very poorly when scaled, tho, especially at smaller scales. Drawing the grid by hand on a canvas has the same scaling artifacts problem as the css. Drawing a grid on an unscaled canvas and redrawing it as necessary avoids scaling artifacts. – silvalli Jun 20 '21 at 00:38
  • @silvalli thanks for your input, but doesn't seem necessary to go over this old answer now (6 years old). It would be way better to post your own answer, where you can detail the limitations of the CSS solution that the OP was asking for, and maybe others would like it better. – José Pereda Jun 20 '21 at 16:09
2

Here is an answer reproduced from an old Oracle forum post.

GridPane based approach

A few methods (base upon a GridPane layout):

  1. style borders of the individual cells (and ensure that they fill their entire grid position) OR
  2. style the background of the whole grid leaving gaps between cells which fill their entire grid position as is shown below OR
  3. add new grid nodes with lines and then style the added lines.

I chose method 2 (styling the grid background) for the code below. The sample uses inline CSS styles (cause I'm lazy), but it would work (and be better) with an external CSS stylesheet to style the grid.

grid

import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class GridPaneStyle extends Application {
    @Override
    public void start(final Stage stage) {
        // create a grid with some sample data.
        GridPane grid = new GridPane();
        grid.addRow(0, new Label("1"), new Label("2"), new Label("3"));
        grid.addRow(1, new Label("A"), new Label("B"), new Label("C"));

        // make all of the Controls and Panes inside the grid fill their grid cell, 
        // align them in the center and give them a filled background.
        // you could also place each of them in their own centered StackPane with 
        // a styled background to achieve the same effect.
        for (Node n : grid.getChildren()) {
            if (n instanceof Control) {
                Control control = (Control) n;
                control.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
                control.setStyle("-fx-background-color: cornsilk; -fx-alignment: center;");
            }
            if (n instanceof Pane) {
                Pane pane = (Pane) n;
                pane.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
                pane.setStyle("-fx-background-color: cornsilk; -fx-alignment: center;");
            }
        }

        // style the grid so that it has a background and gaps around the grid and between the 
        // grid cells so that the background will show through as grid lines.
        grid.setStyle("-fx-background-color: palegreen; -fx-padding: 2; -fx-hgap: 2; -fx-vgap: 2;");
        // turn layout pixel snapping off on the grid so that grid lines will be an even width.
        grid.setSnapToPixel(false);

        // set some constraints so that the grid will fill the available area.
        ColumnConstraints oneThird = new ColumnConstraints();
        oneThird.setPercentWidth(100 / 3.0);
        oneThird.setHalignment(HPos.CENTER);
        grid.getColumnConstraints().addAll(oneThird, oneThird, oneThird);
        RowConstraints oneHalf = new RowConstraints();
        oneHalf.setPercentHeight(100 / 2.0);
        oneHalf.setValignment(VPos.CENTER);
        grid.getRowConstraints().addAll(oneHalf, oneHalf);

        // layout the scene in a stackpane with some padding so that the grid is centered 
        // and it is easy to see the outer grid lines.
        StackPane layout = new StackPane();
        layout.setStyle("-fx-background-color: whitesmoke; -fx-padding: 10;");
        layout.getChildren().addAll(grid);
        stage.setScene(new Scene(layout, 600, 400));
        stage.show();

        // can be uncommented to show the grid lines for debugging purposes, but not particularly useful for styling purposes.
        //grid.setGridLinesVisible(true);
    }

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

Alternate Canvas Based Approach

See also the FXExperience blog post Resizable Grid using Canvas.

jewelsea
  • 150,031
  • 14
  • 366
  • 406
0

The advice to get really sharp grid lines is not correct for all systems. On a Retina display you would have to use a line width of 0.5 and then an offset of 0.25 from the integer line coordinates. So in practice you would have to determine on what system your application is running and then use different line widths and offsets.

mipa
  • 10,369
  • 2
  • 16
  • 35