I am trying to use two for loops to automatically add ImageView nodes to each location. When using the for loops I receive an error. When I comment the for loop code out with only one statement to add an ImageView node the code seems to work can you use for loops to populate GridPane? If so what am I doing wrong? If not what could be used as a solution?
My Class:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class GridCreation extends Application {
#Override
public void start(Stage gameStage) throws Exception {
GridPane grid = new GridPane();
Image backOfCardsImg = new Image("images/naruto_shipuden_logo.png");
ImageView backOfCards = new ImageView(backOfCardsImg);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
grid.add(backOfCards, i, j);
}
}
//grid.add(backOfCards, 1,1);
Scene scene = new Scene(grid);
gameStage.setTitle("MemoryGame");
gameStage.setScene(scene);
gameStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Edit: for the code:
grid.add(backOfCards, i, j);
I changed the line of code to
grid.add(new ImageView(backOfCardImg), i, j);
this seemed to solve the problem but can anyone explain to me why the first option wouldnt work?
This might be a usable start for a memory game in fx. It uses an own extension of ImageView to do the turn and focus animations and to deal with a common backside image. Its only graphics, no game logic.
public class MemoryGame extends Application {
final int rows = 4;
final int columns = 4;
CardView views[][] = new CardView[rows][];
public static class CardView extends ImageView {
static final double scale = 0.95;
static DropShadow shadowhoover = new DropShadow(5, 4, 4, Color.rgb(50, 60, 50));
static DropShadow shadowdown = new DropShadow(2, 2, 2, Color.rgb(50, 60, 50));
static Image backside = null;
public static void setbackside(Image image) { backside = image; }
public CardView(Image image) {
super(backside);
setRotationAxis(new Point3D(0, 200,0));
setScaleX(scale);
setScaleY(scale);
setEffect(shadowdown);
setOnMouseEntered(m -> {
setEffect(shadowhoover);
setScaleX(scale*1.01);
setScaleY(scale*1.01);
});
setOnMouseExited(m -> {
setEffect(shadowdown);
setScaleX(scale);
setScaleY(scale);
});
setOnMouseClicked(m -> {
RotateTransition r1 = new RotateTransition(Duration.millis(300), this);
r1.setByAngle(90);
r1.setOnFinished(e -> setImage(image));
RotateTransition r2 = new RotateTransition(Duration.millis(300), this);
r2.setByAngle(-90);
RotateTransition r3 = new RotateTransition(Duration.millis(300), this);
r3.setByAngle(90);
r3.setOnFinished(e -> setImage(backside));
RotateTransition r4 = new RotateTransition(Duration.millis(300), this);
r4.setByAngle(-90);
new SequentialTransition(r1, r2, new PauseTransition(Duration.millis(1000)), r3, r4).play();
});
}
}
#Override
public void start(Stage gameStage) throws Exception {
GridPane grid = new GridPane();
grid.setBackground(new Background(new BackgroundFill(Color.rgb(140, 200, 140), new CornerRadii(0), new Insets(0))));
grid.setHgap(5);
grid.setVgap(5);
Image back = new Image(MemoryGame.class.getResource("card-back.png").toExternalForm(), 140, 200, true, true);
Image front = new Image(MemoryGame.class.getResource("card-1.png").toExternalForm(), 140, 200, true, true);
CardView.setbackside(back);
for (int r = 0; r < rows; r++) {
views[r] = new CardView[columns];
for (int c = 0; c < columns; c++) {
CardView view = new CardView(front); // different front images of course...
views[r][c] = view;
HBox box = new HBox(5);
box.getChildren().add(views[r][c]);
grid.add(box, c, r);
}
}
//grid.add(backOfCards, 1,1);
Scene scene = new Scene(grid);
gameStage.setTitle("MemoryGame");
gameStage.setScene(scene);
gameStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Related
Long story short, I have 8x8 GridPane (using it as Chess Board) and I want to be able to click on each cell and get its coordinates.
public class BoardView {
private ImageView imageView = new ImageView(new Image("board.png"));
private GridPane boardGrid = new GridPane();
public void createBoard(){
boardGrid.getChildren().add(imageView);
for(int i =0;i < 8; i++){
for(int j = 0; j < 8; j++){
Tile tile = new Tile(i, j);
GridPane.setConstraints(tile.getPane(), i, j);
boardGrid.getChildren().add(tile.getPane());
}
}
}
class Tile {
private int positionX;
private int positionY;
private Pane pane;
Tile(int x, int y) {
pane = new Pane();
positionX = x;
positionY = y;
pane.setOnMouseClicked(e -> {
System.out.println(positionX + " " + positionY);
}
);
}
}
However, everywhere I click, the result is "0 0", not the actual row/column position.
You code is incomplete some of your errors are :
You haven't give a specific size (width, height) on each Pane (Tiles)
I am guessing you set the size of the GridPane somewhere but its just a guess, now the way you add the background image on your Grid is something that I don't recommend instead use a StackPane.
Here is a small example which you can check to debug your problem.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class BoardView extends Application {
// the dimensions of our background Image
private final int BORDER_WIDTH = 695;
private final int BORDER_HEIGHT = 720;
#Override
public void start(Stage stage) throws Exception {
// Load your Image
ImageView backgroundImageView = new ImageView(
new Image("https://cdn.pixabay.com/photo/2013/07/13/10/24/board-157165_960_720.png"));
// Initialize the grid
GridPane boardGrid = initBoard();
// Set the dimensions of the grid
boardGrid.setPrefSize(BORDER_WIDTH, BORDER_HEIGHT);
// Use a StackPane to display the Image and the Grid
StackPane mainPane = new StackPane();
mainPane.getChildren().addAll(backgroundImageView, boardGrid);
stage.setScene(new Scene(mainPane));
stage.setResizable(false);
stage.show();
}
private GridPane initBoard() {
GridPane boardGrid = new GridPane();
int tileNum = 8;
double tileWidth = BORDER_WIDTH / tileNum;
double tileHeight = BORDER_HEIGHT / tileNum;
for (int i = 0; i < tileNum; i++) {
for (int j = 0; j < tileNum; j++) {
Tile tile = new Tile(i, j);
// Set each 'Tile' the width and height
tile.setPrefSize(tileWidth, tileHeight);
// Add node on j column and i row
boardGrid.add(tile, j, i);
}
}
// Return the GridPane
return boardGrid;
}
class Tile extends Pane {
private int positionX;
private int positionY;
public Tile(int x, int y) {
positionX = x;
positionY = y;
setOnMouseClicked(e -> {
System.out.println(positionX + " " + positionY);
});
}
}
public static void main(String[] args) {
launch(args);
}
}
From my point of view you its more easy to handle each Tile if you made the class to extend the Pane instead of just holding a reference to it but this is just my opinion. Well the above its just an example anyway. If you cant find the problem then post a MCVE show we can help you better.
I am creating a chess game and i am stuck at one thing for a couple of days.
So i want to create event when user moves over a figure the image of figure replaces to another image with border and when user moves away the image must return to normal. Here is full code.
Folder for Project is called sample,Folder for images is Called Sprites.
Folder for classes is called Figures.I will link the images for black pawn.
package sample;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import sample.Figures.*;
public class Main extends Application {
#Override
public void start(Stage primaryStage){
primaryStage.setTitle("ChessGame");
primaryStage.getIcons().add(new Image("/sample/Chess-icon.png"));
GridPane root = new GridPane();
final GridPane group = new GridPane();
group.setPadding(new Insets(15, 25, 25, 25));
for (int i = 0 ; i < 8 ; i++) {
for (int j = 0 ; j < 8 ; j++) {
Rectangle rectangle = new Rectangle( 50, 50);
if(j % 2 == 0 && (i % 2 == 0)) {
rectangle.setFill(Color.BEIGE);
}
else if(!((j + 2) % 2 == 0) && !((i + 2) % 2 == 0)) {
rectangle.setFill(Color.BEIGE);
}
else {
rectangle.setFill(Color.GRAY);
}
group.add(rectangle,i,j);
}
}
//FIGURES
//Black
//Pawns
final blackPawn BlackP_1 = new blackPawn(0,1,64,65);
group.add(BlackP_1.IMG,BlackP_1.x,BlackP_1.y);
BlackP_1.IMG.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
group.getChildren().remove(64,65);
group.add(BlackP_1.IMGglow,0,1);
}
});
BlackP_1.IMGglow.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
group.getChildren().remove(64,65);
group.add(BlackP_1.IMG,BlackP_1.x,BlackP_1.y);
}
});
blackPawn BlackP_2 = new blackPawn(1,1);
group.add(BlackP_2.IMG,BlackP_2.x,BlackP_2.y);
blackPawn BlackP_3 = new blackPawn(2,1);
group.add(BlackP_3.IMG,BlackP_3.x,BlackP_3.y);
blackPawn BlackP_4 = new blackPawn(3,1);
group.add(BlackP_4.IMG,BlackP_4.x,BlackP_4.y);
blackPawn BlackP_5 = new blackPawn(4,1);
group.add(BlackP_5.IMG,BlackP_5.x,BlackP_5.y);
blackPawn BlackP_6 = new blackPawn(5,1);
group.add(BlackP_6.IMG,BlackP_6.x,BlackP_6.y);
blackPawn BlackP_7 = new blackPawn(6,1);
group.add(BlackP_7.IMG,BlackP_7.x,BlackP_7.y);
blackPawn BlackP_8 = new blackPawn(7,1);
group.add(BlackP_8.IMG,BlackP_8.x,BlackP_8.y);
//Rooks
blackRook BlackR_1 = new blackRook();
group.add(BlackR_1.IMG,7,0);
blackRook BlackR_2 = new blackRook();
group.add(BlackR_2.IMG,0,0);
//Knights
blackKnight BlackK_1 = new blackKnight();
group.add(BlackK_1.IMG,1,0);
blackKnight BlackK_2 = new blackKnight();
group.add(BlackK_2.IMG,6,0);
//Bishop
blackBishop BlackB_1 = new blackBishop();
group.add(BlackE_1.IMG,2,0);
blackBishop BlackB_2 = new blackBishop();
group.add(BlackE_2.IMG,5,0);
//Queen
blackQueen blackQueen= new blackQueen();
group.add(blackQueen.IMG,3,0);
//King
blackKing blackking = new blackKing();
group.add(blackking.IMG,4,0);
//WHITE
//Pawns
final whitePawn WhiteP_1 = new whitePawn();
group.add(WhiteP_1.IMG,0,6);
whitePawn WhiteP_2 = new whitePawn();
group.add(WhiteP_2.IMG,1,6);
whitePawn WhiteP_3 = new whitePawn();
group.add(WhiteP_3.IMG,2,6);
whitePawn WhiteP_4 = new whitePawn();
group.add(WhiteP_4.IMG,3,6);
whitePawn WhiteP_5 = new whitePawn();
group.add(WhiteP_5.IMG,4,6);
whitePawn WhiteP_6 = new whitePawn();
group.add(WhiteP_6.IMG,5,6);
whitePawn WhiteP_7 = new whitePawn();
group.add(WhiteP_7.IMG,6,6);
whitePawn WhiteP_8 = new whitePawn();
group.add(WhiteP_8.IMG,7,6);
//Rooks
whiteRook WhiteR_1 = new whiteRook();
group.add(WhiteR_1.IMG,0,7);
whiteRook WhiteR_2 = new whiteRook();
group.add(WhiteR_2.IMG,7,7);
//Knights
whiteKnight WhiteK_1 = new whiteKnight();
group.add(WhiteK_1.IMG,1,7);
whiteKnight WhiteK_2 = new whiteKnight();
group.add(WhiteK_2.IMG,6,7);
//Bishop
whiteBishop WhiteB_1 = new whiteBishop();
group.add(WhiteB_1.IMG,2,7);
whiteBishop WhiteB_2 = new whiteBishop();
group.add(WhiteB_2.IMG,5,7);
//Queen
whiteQueen whitequeen = new whiteQueen();
group.add(whitequeen.IMG,3,7);
//King
whiteKing whiteking = new whiteKing();
group.add(whiteking.IMG,4,7);
root.getChildren().add(group);
root.setStyle("-fx-background-color: #C1D1E8;");
Scene scene = new Scene(root, 450, 440);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Black Pawn Class
package sample.Figures;
import javafx.scene.image.ImageView;
public class blackPawn {
public int x;
public int y;
public int start;
public int end;
public ImageView IMG = new ImageView("sample/Sprites/blackPawn.png");
public ImageView IMGglow = new ImageView("sample/Sprites/blackPawnStroke.png");
public blackPawn(int x,int y)
{
this.x = x;
this.y = y;
}
public blackPawn(int x,int y,int start,int end)
{
this.x = x;
this.y = y;
this.start = start;
this.end = end;
}
}
It's quite easy to change the Image of an ImageView just by calling the setImage(). There are two ways to make the transition from one state to another. The first approach is programmatically through setOnMouseEntered() and setOnMouseExited() here is an example :
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class HoverableImage extends Application {
#Override
public void start(Stage stage) throws Exception {
ImageView imageView = createChestImage();
FlowPane pane = new FlowPane();
pane.setAlignment(Pos.CENTER);
pane.getChildren().add(imageView);
stage.setScene(new Scene(pane, 100, 100));
stage.show();
}
private ImageView createChestImage() {
ImageView iv = new ImageView(new Image("https://i.stack.imgur.com/rd71Q.png"));
iv.setOnMouseEntered(e->{
iv.setImage(new Image("https://i.stack.imgur.com/7JU7r.png"));
});
iv.setOnMouseExited(e->{
iv.setImage(new Image("https://i.stack.imgur.com/rd71Q.png"));
});
return iv;
}
public static void main(String[] args) {
launch(args);
}
}
The second approach would be to do that using CSS, you can set each chest piece an ID (for example : iv.setID("SoldierPiece"); ) and then apply those CSS rules on it :
#SoldierPiece{
-fx-image: url("https://i.stack.imgur.com/rd71Q.png");
}
#SoldierPiece:hover{
-fx-image: url("https://i.stack.imgur.com/7JU7r.png");
}
I am trying to do a neural network training visualization with JavaFX 8. The network cells are shown as dots changing their color depending on the output value. The calculations and the drawing are done in a thread that can be started or stopped by clicking a button. For a while everything works fine but after a number of iterations the display is no longer updated.
What needs to be done to reliably update the display?
I am using JRE version 1.8.0_45.
Here's a simplified version of my code:
import javafx.application.*;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.stage.Stage;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.*;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
public class TestView extends Application {
public static final int SCENE_WIDTH = 1000;
public static final int SCENE_HEIGHT = 800;
public static final int BUTTON_PANEL_HEIGHT = 80;
private Canvas canvas;
private GraphicsContext gc;
private Task<Void> task = null;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("MLP");
BorderPane borderPane = new BorderPane();
canvas = new Canvas( SCENE_WIDTH, 3*SCENE_HEIGHT-BUTTON_PANEL_HEIGHT );
gc = canvas.getGraphicsContext2D();
borderPane.setCenter(new ScrollPane(canvas));
GridPane buttonPanel = new GridPane();
Button buttonTrain = new Button("Train");
buttonTrain.setMinWidth(SCENE_WIDTH/2);
buttonPanel.setPadding(new Insets(0, 0, 0, 0));
buttonPanel.add(buttonTrain, 1, 0);
borderPane.setBottom(buttonPanel);
buttonTrain.setOnMouseClicked( e -> {
if (task != null) {
task.cancel();
task = null;
}
else {
task = new Task<Void>() {
#Override
protected Void call() throws Exception {
for (int i = 1; i <= 10000000; i++) {
if (isCancelled()) {
break;
}
// dummy calculation
doSomeStuff();
// dummy graphics update
gc.setFill(Color.GREEN);
gc.fillOval(50, 50, 20, 20);
gc.clearRect(200, 10, 200, 100);
gc.setFill(Color.BLACK);
gc.fillText("" + i, 200, 50);
}
return null;
}
};
new Thread(task).start();
}
});
Scene scene = new Scene( borderPane, SCENE_WIDTH, SCENE_HEIGHT );
primaryStage.setScene(scene);
primaryStage.show();
}
private double doSomeStuff() {
double r = 0.5;
for ( int i = 0; i < 10000; i++ ) {
r = Math.sin(r);
}
return r;
}
}
So, I am trying to display a chessboard in javaFX. I will have to perform different operations and draw on some of the tiles so I chose to use a Canvas for each tile and a GridPane to arrange them for me in a grid fashion.
Unfortunately I am having some problems with the resizing of the grid tiles; I want my whole chessboard to automatically adapt its size to the Scene. Therefore, I have added a ChangeListener to both the height and width properties of the GridPane which takes care of resizing the tiles. This only works when the window gets bigger, when the window is reduced to a smaller size everything still gets bigger!
Here's the shortest SSCCE I came up with which reproduces my problem:
package chessboardtest;
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.geometry.*;
import javafx.scene.*;
import javafx.scene.canvas.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.stage.Stage;
public class ChessboardTest extends Application {
final int size = 10;
#Override
public void start(Stage primaryStage) {
VBox root = new VBox();
final GridPane chessboard = new GridPane();
fillChessboard(chessboard, size);
ChangeListener<Number> resizeListener = new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
double newWidth = chessboard.getWidth() / size;
double newHeight = chessboard.getHeight() / size;
for(Node n: chessboard.getChildren()) {
Canvas canvas = (Canvas)n;
canvas.setWidth(newWidth);
canvas.setHeight(newHeight);
}
}
};
chessboard.widthProperty().addListener(resizeListener);
chessboard.heightProperty().addListener(resizeListener);
root.getChildren().add(chessboard);
root.setPadding(new Insets(10));
VBox.setVgrow(chessboard, Priority.ALWAYS);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("chessboard");
primaryStage.setScene(scene);
primaryStage.show();
}
void fillChessboard(GridPane pane, int size) {
class RedrawListener implements ChangeListener<Number> {
Color color;
Canvas canvas;
public RedrawListener(Canvas c, int i) {
if(i % 2 == 0) {
color = Color.BLACK;
}
else {
color = Color.WHITE;
}
canvas = c;
}
#Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
canvas.getGraphicsContext2D().setFill(color);
canvas.getGraphicsContext2D().fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
}
}
for(int row = 0; row < size; row++) {
for(int col = 0, i = row; col < size; col++, i++) {
Canvas c = new Canvas();
RedrawListener rl = new RedrawListener(c, i);
c.widthProperty().addListener(rl);
c.heightProperty().addListener(rl);
pane.add(c, row, col);
}
}
}
public static void main(String[] args) {
launch(args);
}
}
If you don't need a canvas (and you probably don't), just use StackPanes for the squares and make them fill the width and the height. You can always add a canvas (or anything else) to the StackPanes to display their content.
import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.control.Control;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Chessboard extends Application {
#Override
public void start(Stage primaryStage) {
GridPane root = new GridPane();
final int size = 8 ;
for (int row = 0; row < size; row++) {
for (int col = 0; col < size; col ++) {
StackPane square = new StackPane();
String color ;
if ((row + col) % 2 == 0) {
color = "white";
} else {
color = "black";
}
square.setStyle("-fx-background-color: "+color+";");
root.add(square, col, row);
}
}
for (int i = 0; i < size; i++) {
root.getColumnConstraints().add(new ColumnConstraints(5, Control.USE_COMPUTED_SIZE, Double.POSITIVE_INFINITY, Priority.ALWAYS, HPos.CENTER, true));
root.getRowConstraints().add(new RowConstraints(5, Control.USE_COMPUTED_SIZE, Double.POSITIVE_INFINITY, Priority.ALWAYS, VPos.CENTER, true));
}
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
This is a nice solution, but resizing is so much easier with data binding in Java FX. You can hide all listener business this way.
Here is a solution much like James D's, but using Rectangles insread of Canvases for the squares:
public class ResizeChessboard extends Application {
GridPane root = new GridPane();
final int size = 8;
public void start(Stage primaryStage) {
for (int row = 0; row < size; row++) {
for (int col = 0; col < size; col++) {
Rectangle square = new Rectangle();
Color color;
if ((row + col) % 2 == 0) color = Color.WHITE;
else color = Color.BLACK;
square.setFill(color);
root.add(square, col, row);
square.widthProperty().bind(root.widthProperty().divide(size));
square.heightProperty().bind(root.heightProperty().divide(size));
}
}
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I would like to display a grid containing a various number of rectangles in JavaFX. It is important that this grid cannot be resized.
I chose the GridPane layout. I dynamically add javafx.scene.shape.Rectangle to it. Here's how my grid looks like with 2 rows and 4 columns.
Upon resizing, I would like it to keep the same overall shape, that is to say each Rectangle having the same size and keeping an horizontal and vertical gaps between my Rectangles.
However, here's what I get with a 4x4 grid:
The problems being:
The last row and last column do not have the same size as the rest of the Rectangles.
The gaps have disappeared.
Here is my code responsible for refreshing the display:
public void refreshConstraints() {
getRowConstraints().clear();
getColumnConstraints().clear();
for (int i = 0; i < nbRow; i++) {
RowConstraints rConstraint = new RowConstraints();
// ((nbRow - 1) * 10 / nbRow) = takes gap into account (10% of height)
rConstraint.setPercentHeight(100 / nbRow - ((nbRow - 1) * 10 / nbRow));
getRowConstraints().add(rConstraint);
}
for (int i = 0; i < nbColumn; i++) {
ColumnConstraints cConstraint = new ColumnConstraints();
cConstraint.setPercentWidth(100 / nbColumn - ((nbColumn - 1) * 10 / nbColumn));
getColumnConstraints().add(cConstraint);
}
}
Using the setFillWidth and setHgrow yields no result either, the gap is kept between my Rectangles, but the Rectangles aren't resized and they overlap the rest of my GUI elements.
EDIT: MCVE code:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.RowConstraints;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class DynamicGrid extends Application {
//Class containing grid (see below)
private GridDisplay gridDisplay;
#Override
public void start(Stage primaryStage) {
//Represents the grid with Rectangles
gridDisplay = new GridDisplay(400, 200);
//Fields to specify number of rows/columns
TextField rowField = new TextField();
TextField columnField = new TextField();
//Function to set an action when text field loses focus
buildTextFieldActions(rowField, columnField);
HBox fields = new HBox();
fields.getChildren().add(rowField);
fields.getChildren().add(new Label("x"));
fields.getChildren().add(columnField);
BorderPane mainPanel = new BorderPane();
mainPanel.setLeft(gridDisplay.getDisplay());
mainPanel.setBottom(fields);
Scene scene = new Scene(mainPanel);
primaryStage.setTitle("Test grid display");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
private void buildTextFieldActions(final TextField rowField, final TextField columnField) {
rowField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
if (!t1) {
if (!rowField.getText().equals("")) {
try {
int nbRow = Integer.parseInt(rowField.getText());
gridDisplay.setRows(nbRow);
gridDisplay.updateDisplay();
} catch (NumberFormatException nfe) {
System.out.println("Please enter a valid number.");
}
}
}
}
});
columnField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
if (!t1) {
if (!columnField.getText().equals("")) {
try {
int nbColumn = Integer.parseInt(columnField.getText());
gridDisplay.setColumns(nbColumn);
gridDisplay.updateDisplay();
} catch (NumberFormatException nfe) {
System.out.println("Please enter a valid number.");
}
}
}
}
});
}
//Class responsible for displaying the grid containing the Rectangles
public class GridDisplay {
private GridPane gridPane;
private int nbRow;
private int nbColumn;
private int width;
private int height;
private double hGap;
private double vGap;
public GridDisplay(int width, int height) {
this.gridPane = new GridPane();
this.width = width;
this.height = height;
build();
}
private void build() {
this.hGap = 0.1 * width;
this.vGap = 0.1 * height;
gridPane.setVgap(vGap);
gridPane.setHgap(hGap);
gridPane.setPrefSize(width, height);
initializeDisplay(width, height);
}
//Builds the first display (correctly) : adds a Rectangle for the number
//of rows and columns
private void initializeDisplay(int width, int height) {
nbRow = height / 100;
nbColumn = width / 100;
for (int i = 0; i < nbColumn; i++) {
for (int j = 0; j < nbRow; j++) {
Rectangle rectangle = new Rectangle(100, 100);
rectangle.setStroke(Paint.valueOf("orange"));
rectangle.setFill(Paint.valueOf("steelblue"));
gridPane.add(rectangle, i, j);
}
}
}
//Function detailed in post
//Called in updateDisplay()
public void refreshConstraints() {
gridPane.getRowConstraints().clear();
gridPane.getColumnConstraints().clear();
for (int i = 0; i < nbRow; i++) {
RowConstraints rConstraint = new RowConstraints();
rConstraint.setPercentHeight(100 / nbRow - ((nbRow - 1) * 10 / nbRow));
gridPane.getRowConstraints().add(rConstraint);
}
for (int i = 0; i < nbColumn; i++) {
ColumnConstraints cConstraint = new ColumnConstraints();
cConstraint.setPercentWidth(100 / nbColumn - ((nbColumn - 1) * 10 / nbColumn));
gridPane.getColumnConstraints().add(cConstraint);
}
}
public void setColumns(int newColumns) {
nbColumn = newColumns;
}
public void setRows(int newRows) {
nbRow = newRows;
}
public GridPane getDisplay() {
return gridPane;
}
//Function called when refreshing the display
public void updateDisplay() {
Platform.runLater(new Runnable() {
#Override
public void run() {
//The gridpane is cleared of the previous children
gridPane.getChildren().clear();
//A new rectangle is added for row*column
for (int i = 0; i < nbColumn; i++) {
for (int j = 0; j < nbRow; j++) {
Rectangle rectangle = new Rectangle(100, 100);
rectangle.setStroke(Paint.valueOf("orange"));
rectangle.setFill(Paint.valueOf("steelblue"));
gridPane.add(rectangle, i, j);
}
}
//Call to this function to update the grid's constraints
refreshConstraints();
}
});
}
}
}
Seems like a TilePane is a better fit for this use case than a GridPane.
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.TilePane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
// java 8 code
public class DynamicTiles extends Application {
//Class containing grid (see below)
private GridDisplay gridDisplay;
//Class responsible for displaying the grid containing the Rectangles
public class GridDisplay {
private static final double ELEMENT_SIZE = 100;
private static final double GAP = ELEMENT_SIZE / 10;
private TilePane tilePane = new TilePane();
private Group display = new Group(tilePane);
private int nRows;
private int nCols;
public GridDisplay(int nRows, int nCols) {
tilePane.setStyle("-fx-background-color: rgba(255, 215, 0, 0.1);");
tilePane.setHgap(GAP);
tilePane.setVgap(GAP);
setColumns(nCols);
setRows(nRows);
}
public void setColumns(int newColumns) {
nCols = newColumns;
tilePane.setPrefColumns(nCols);
createElements();
}
public void setRows(int newRows) {
nRows = newRows;
tilePane.setPrefRows(nRows);
createElements();
}
public Group getDisplay() {
return display;
}
private void createElements() {
tilePane.getChildren().clear();
for (int i = 0; i < nCols; i++) {
for (int j = 0; j < nRows; j++) {
tilePane.getChildren().add(createElement());
}
}
}
private Rectangle createElement() {
Rectangle rectangle = new Rectangle(ELEMENT_SIZE, ELEMENT_SIZE);
rectangle.setStroke(Color.ORANGE);
rectangle.setFill(Color.STEELBLUE);
return rectangle;
}
}
#Override
public void start(Stage primaryStage) {
//Represents the grid with Rectangles
gridDisplay = new GridDisplay(2, 4);
//Fields to specify number of rows/columns
TextField rowField = new TextField("2");
TextField columnField = new TextField("4");
//Function to set an action when text field loses focus
buildTextFieldActions(rowField, columnField);
HBox fields = new HBox(10);
fields.getChildren().add(rowField);
fields.getChildren().add(new Label("x"));
fields.getChildren().add(columnField);
BorderPane mainPanel = new BorderPane();
mainPanel.setCenter(gridDisplay.getDisplay());
mainPanel.setTop(fields);
Scene scene = new Scene(mainPanel, 1000, 800);
primaryStage.setTitle("Test grid display");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
private void buildTextFieldActions(final TextField rowField, final TextField columnField) {
rowField.focusedProperty().addListener((ov, t, t1) -> {
if (!t1) {
if (!rowField.getText().equals("")) {
try {
int nbRow = Integer.parseInt(rowField.getText());
gridDisplay.setRows(nbRow);
} catch (NumberFormatException nfe) {
System.out.println("Please enter a valid number.");
}
}
}
});
columnField.focusedProperty().addListener((ov, t, t1) -> {
if (!t1) {
if (!columnField.getText().equals("")) {
try {
int nbColumn = Integer.parseInt(columnField.getText());
gridDisplay.setColumns(nbColumn);
} catch (NumberFormatException nfe) {
System.out.println("Please enter a valid number.");
}
}
}
});
}
}
Thanks a lot for your answer. TilePanes are indeed a lot easier to use, although what you've written does not completely answer my question.
I wanted to have a pane in which the children would resize, and not the pane itself. It seems setting the maxSize and prefSize doesn't have any effect.
EDIT: I managed to do it using two JavaFX Property in my GridDisplay class, corresponding to the fixed height and width of my grid:
public class GridDisplay {
private ReadOnlyDoubleProperty heightProperty;
private ReadOnlyDoubleProperty widthProperty;
...
}
Then I assign to these members the values corresponding to the desired fixed size in the constructor. The size of the children inside the grid correspond to a fraction of the height and width of the grid, depending on the number of rows and columns. Here's what my updateDisplay() looks like:
public void updateDisplay() {
gridPane.getChildren().clear();
for (int i = 0; i < nbColumn; i++) {
for (int j = 0; j < nbRow; j++) {
Rectangle rectangle = new Rectangle(100, 100);
//Binding the fraction of the grid size to the width
//and heightProperty of the child
rectangle.widthProperty().bind(widthProperty.divide(nbColumn));
rectangle.heightProperty().bind(heightProperty.divide(nbRow));
gridPane.add(rectangle, i, j);
}
}
}