Display 2d array as grid in JavaFX
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);
}
}
}
Joe Morgan
Updated on June 04, 2022Comments
-
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 over 9 yearsSorry 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 arrayMyNode[][] playfield = new MyNode[n][m];
? @lolrand -
Roland over 9 yearsYes. 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 over 9 yearsSorry 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 over 9 yearsAdded 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 over 9 yearsOkay 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 theplayfield
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