Display 2d array as grid in JavaFX

12,873

I wouldn't use a control. I'd rather create a node for each of the items in the array and put them on the scene. Something like this:

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;


public class NodeDemo extends Application {

    private double sceneWidth = 1024;
    private double sceneHeight = 768;

    private int n = 10;
    private int m = 10;

    double gridWidth = sceneWidth / n;
    double gridHeight = sceneHeight / m;

    MyNode[][] playfield = new MyNode[n][m];

    @Override
    public void start(Stage primaryStage) {


        Group root = new Group();

        // initialize playfield
        for( int i=0; i < n; i++) {
            for( int j=0; j < m; j++) {

                // create node
                MyNode node = new MyNode( "Item " + i + "/" + j, i * gridWidth, j * gridHeight, gridWidth, gridHeight);

                // add node to group
                root.getChildren().add( node);

                // add to playfield for further reference using an array
                playfield[i][j] = node;

            }
        }


        Scene scene = new Scene( root, sceneWidth, sceneHeight);

        primaryStage.setScene( scene);
        primaryStage.show();

    }

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

    public static class MyNode extends StackPane {

        public MyNode( String name, double x, double y, double width, double height) {

            // create rectangle
            Rectangle rectangle = new Rectangle( width, height);
            rectangle.setStroke(Color.BLACK);
            rectangle.setFill(Color.LIGHTBLUE);

            // create label
            Label label = new Label( name);

            // set position
            setTranslateX( x);
            setTranslateY( y);

            getChildren().addAll( rectangle, label);

        }

    }

}

This way you can create animated movement of the nodes easily with a PathTransition. Like this shuffle mechanism:

import java.util.Random;

import javafx.animation.Animation.Status;
import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;


public class NodeDemo extends Application {

    private double sceneWidth = 1024;
    private double sceneHeight = 768;

    private int n = 10;
    private int m = 10;

    double gridWidth = sceneWidth / n;
    double gridHeight = sceneHeight / m;

    MyNode[][] playfield = new MyNode[n][m];

    @Override
    public void start(Stage primaryStage) {


        Group root = new Group();

        // initialize playfield
        for( int i=0; i < n; i++) {
            for( int j=0; j < m; j++) {

                // create node
                MyNode node = new MyNode( "Item " + i + "/" + j, i * gridWidth, j * gridHeight, gridWidth, gridHeight);

                // add node to group
                root.getChildren().add( node);

                // add to playfield for further reference using an array
                playfield[i][j] = node;

            }
        }


        Scene scene = new Scene( root, sceneWidth, sceneHeight);

        primaryStage.setScene( scene);
        primaryStage.show();


        animate();

    }

    private void animate() {

         Random random = new Random();

         int ai = random.nextInt(n);
         int aj = random.nextInt(m);

         int bi = random.nextInt(n);
         int bj = random.nextInt(m);

         // make sure that A and B are never the same
         if( ai == bi && aj == bj) {
             ai++;
             if( ai >= n)
                 ai = 0;
         }

         MyNode nodeA = playfield[ai][aj];
         nodeA.toFront();

         MyNode nodeB = playfield[bi][bj];
         nodeB.toFront();

         // swap on array to keep array consistent
         playfield[ai][aj] = nodeB;
         playfield[bi][bj] = nodeA;

         // A -> B
         Path pathA = new Path();
         pathA.getElements().add (new MoveTo ( nodeA.getTranslateX() + nodeA.getBoundsInParent().getWidth() / 2.0, nodeA.getTranslateY() + nodeA.getBoundsInParent().getHeight() / 2.0));
         pathA.getElements().add (new LineTo( nodeB.getTranslateX() + nodeB.getBoundsInParent().getWidth() / 2.0, nodeB.getTranslateY() + nodeB.getBoundsInParent().getHeight() / 2.0));

         PathTransition pathTransitionA = new PathTransition(); 
         pathTransitionA.setDuration(Duration.millis(1000));
         pathTransitionA.setNode( nodeA);
         pathTransitionA.setPath(pathA);

         pathTransitionA.play();

         // B -> A
         Path pathB = new Path();
         pathB.getElements().add (new MoveTo ( nodeB.getTranslateX() + nodeB.getBoundsInParent().getWidth() / 2.0, nodeB.getTranslateY() + nodeB.getBoundsInParent().getHeight() / 2.0));
         pathB.getElements().add (new LineTo( nodeA.getTranslateX() + nodeA.getBoundsInParent().getWidth() / 2.0, nodeA.getTranslateY() + nodeA.getBoundsInParent().getHeight() / 2.0));

         PathTransition pathTransitionB = new PathTransition(); 
         pathTransitionB.setDuration(Duration.millis(1000));
         pathTransitionB.setNode( nodeB);
         pathTransitionB.setPath(pathB);

         pathTransitionB.play();

         pathTransitionA.setOnFinished( new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {

                if( pathTransitionB.getStatus() == Status.RUNNING)
                    return;

                animate();
            }
        });

         pathTransitionB.setOnFinished( new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {

                if( pathTransitionA.getStatus() == Status.RUNNING)
                    return;

                animate();
            }
        });

    }

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

    public static class MyNode extends StackPane {

        public MyNode( String name, double x, double y, double width, double height) {

            // create rectangle
            Rectangle rectangle = new Rectangle( width, height);
            rectangle.setStroke(Color.BLACK);
            rectangle.setFill(Color.LIGHTBLUE);

            // create label
            Label label = new Label( name);

            // set position
            setTranslateX( x);
            setTranslateY( y);

            getChildren().addAll( rectangle, label);

        }

    }

}

And here's an example about how you could handle the cells via subclassing. But that's just one way to do it:

public class NodeDemo extends Application {

    private double sceneWidth = 1024;
    private double sceneHeight = 768;

    private int n = 10;
    private int m = 10;

    double gridWidth = sceneWidth / n;
    double gridHeight = sceneHeight / m;

    MyNode[][] playfield = new MyNode[n][m];


    @Override
    public void start(Stage primaryStage) {


        Group root = new Group();

        // initialize playfield
        for( int i=0; i < n; i++) {
            for( int j=0; j < m; j++) {

                MyNode node = null;

                // create bug
                if( i == 0 && j == 0) {

                    node = new Bug( "Bug", Color.ORANGE, i, j);

                }
                // create food
                else if( i == 5 && j == 5) {

                    node = new Food( "Food", Color.GREEN, i, j);

                }
                // create obstacle
                else if( i == 3 && j == 3) {

                    node = new Obstacle( "Obstacle", Color.GRAY, i, j);

                } 

                // add node to group
                if( node != null) {

                    root.getChildren().add( node);

                    // add to playfield for further reference using an array
                    playfield[i][j] = node;

                }


            }
        }


        Scene scene = new Scene( root, sceneWidth, sceneHeight);

        primaryStage.setScene( scene);
        primaryStage.show();


        // move bugs
        animate();

    }

    private void animate() {
        // TODO
    }

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

    private class Food extends MyNode {

        public Food(String name, Color color, double x, double y) {
            super(name, color, x, y);
        }


    }

    private class Obstacle extends MyNode {

        public Obstacle(String name, Color color, double x, double y) {
            super(name, color, x, y);
        }


    }

    private class Bug extends MyNode {

        public Bug(String name, Color color, double x, double y) {
            super(name, color, x, y);
        }


    }

    private class MyNode extends StackPane {

        public MyNode( String name, Color color, double x, double y) {

            // create rectangle
            Rectangle rectangle = new Rectangle( gridWidth, gridHeight);
            rectangle.setStroke( color);
            rectangle.setFill( color.deriveColor(1, 1, 1, 0.7));

            // create label
            Label label = new Label( name);

            // set position
            setTranslateX( x * gridWidth);
            setTranslateY( y * gridHeight);

            getChildren().addAll( rectangle, label);

        }

    }

}
Share:
12,873
Joe Morgan
Author by

Joe Morgan

Updated on June 04, 2022

Comments

  • Joe Morgan
    Joe Morgan about 2 years

    for my Java coursework I have to create a grid based animation. Basically I currently have a 2d array containing certain values, which change each time the program is run. For example it could be a 20x20 2d array, or a 32x32 etc. Inside the array certain values are stored, chars represent animals, and numbers represent food. The animals smell the food and then move towards the food after each cycle of the program, hence their position in the array change after each cycle. How the program works isn't really relevant to the question I'm asking.

    Basically I now have to implement this in JavaFX (it currently works in the console, displaying the array as a grid each cycle). I was just wondering which control would be best to use in JavaFX to display a 2d array, or if perhaps someone could point me in the right direction of how to start coding this?

    I'm new to java (and JavaFX) so am not sure of which controls to use...

  • Joe Morgan
    Joe Morgan over 9 years
    Sorry for the late reply! Thanks a lot for the method you've described, I've played around with it and I think I can make it work. At the moment I have a 2d string array called 'AWorld' containing different characters with represent different things. e.g. a lower case letter represents and animal, a blank space represents a an empty space, a 1 represents a piece of food, a 0 represents an obstacle. What I need to do is convert this string array (AWorld) which looks like String[][] worldArray = new String[x][y]; into a node array MyNode[][] playfield = new MyNode[n][m];? @lolrand
  • Roland
    Roland over 9 years
    Yes. And I wouldn't do it with a String array at all. I'd subclass e. g. MyNode or create an interface and use Java objects instead of the strings.
  • Joe Morgan
    Joe Morgan over 9 years
    Sorry for my naivety, but could you go into a bit more detail about how to subclass? Do you mean have a different class for Food, Obstacle, Bug etc? If so how would I put these different types into MyNode? @lolrand
  • Roland
    Roland over 9 years
    Added the example, see code above. What's left to do is to change the position of the objects in the array and while you do that perform the movement animation.
  • Joe Morgan
    Joe Morgan over 9 years
    Okay thanks a lot, I'll have a look at it now. One problem I'm running into though is how to update the scene each time to map is updated. I have a function that moves the bugs and therefore their position in worldArray changes. So instead of setting up the playfield and then animating it at random, I need to set up the playfield, display the playfield and then update and display the playfield in realtime... So basically the playfield is constantly updating and bugs are constantly moving depending on their positions in the playfield array. I realise this is a lot to ask... @lolrand