Got a GridPane with TextFields in which I want to change the size of them, so they can match they squares the grid creates.
GridPane grid = new GridPane();
grid.setPadding(new Insets(50,50,50,50));
TextField[][] textFields = new TextField[9][9];
//En loop för att generera fältet börjar här
for(int x =0; x < 9; x++){
for(int y= 0; y < 9; y++){
grid.setStyle("-fx-background-color: black, -fx-control-inner-background; -fx-background-insets: 0, 2; -fx-padding: 2;");
TextField textField = new TextField("");
textField.setStyle("-fx-pref-width: 10em;");
textField.setStyle("-fx-pref-height: 10em;");
textField.setFont(Font.font("Serif", FontWeight.LIGHT, 20));
GridPane.setConstraints(textField, y, x);
grid.getChildren().add(textField);
textFields[x][y]=textField;
textFields[x][y].setText("" + KeepingUpWithTheSudokus.getNum(x, y));
// textFields[x][y].setPrefHeight(100);
// textFields[x][y].setPrefWidth(20);
}
}
Tried by using setPrefWidth & Height but left the grid only stretched at the height. Searching for a simple way to change the size.
Related
I am new to JavaFx and I wanted to know how to draw a grid, where I want to draw points on the grid corners. Should I use a gridpane as a foundation or a linechart ? What are the best classes to use a grid and draw on it ?
I wouldn’t use a GridPane, as its children are not guaranteed to be the same size, only to have their grid cell edges aligned.
A TilePane, however, does guarantee that its cells are the same size. You can then use a Group to combine the TilePane with nodes, such as Circles, which are centered on the points between the grid cells using some basic math:
public class Grid
extends Application {
private int rows = 10;
private int columns = 10;
private int spacing = 8;
#Override
public void start(Stage stage) {
TilePane pane = new TilePane(spacing, spacing);
pane.setPrefColumns(columns);
Group group = new Group(pane);
for (int row = 1; row < rows; row++) {
for (int col = 1; col < columns; col++) {
Circle point = new Circle(2);
point.setFill(Color.BLACK);
// x = ((tilewidth + hgap) * col) - (hgap / 2)
// y = ((tileheight + vgap) * row) - (vgap / 2)
point.centerXProperty().bind(
pane.tileWidthProperty().add(pane.hgapProperty())
.multiply(col)
.subtract(pane.hgapProperty().divide(2)));
point.centerYProperty().bind(
pane.tileHeightProperty().add(pane.vgapProperty())
.multiply(row)
.subtract(pane.vgapProperty().divide(2)));
group.getChildren().add(point);
}
}
// Example grid content
for (int row = 0; row < rows; row++) {
for (int col = 0; col < columns; col++) {
Text text = new Text(String.valueOf(row * rows + col));
pane.getChildren().add(text);
}
}
stage.setScene(new Scene(group));
stage.setTitle("Grid");
stage.show();
}
}
I have to use a double array to make 49 squares. What I have only given me one rectangle.
Rectangle[][] rectArray = new Rectangle[7][7];
//grid is a GridPane containing 49 rectangles.
GridPane grid = new GridPane();
//---- add 49 rectangles to the grid pane, it is recommended to use nested loops
for(int i = 0; i < rectArray.length; i++)
{
for(int j = 0; j < rectArray.length; j++)
{
rectArray[i][j] = new Rectangle(470/7,390/7);
rectArray[i][j].setStroke(Color.BLACK);
rectArray[i][j].setFill(Color.WHITE);
grid.getChildren().add(rectArray[i][j]);
}
}
Add
GridPane.setConstraints(rectArray[i][j], i, j);
right before you add the rectangle to the grid. Right now all the rectangles are put at the same position (0, 0), so they overlap and look like one.
I am trying to build a battle ship application. So far I managed to display both fields for the player and the enemy. A field consists of 10 x 10 Rectangles - 10 HBox'es in one VBox. This is the method that creates me a field:
private Rectangle[][] field;
public VBox CreateField(){
VBox vbox = new VBox(2);
for(int i = 0; i < this.columns; ++i){
HBox hbox = new HBox(2);
for(int j = 0; j < this.rows; ++j){
this.array[j][i] = 0;
this.field[i][j] = new Rectangle(this.height*j, this.width*i, this.height, this.width);
this.field[i][j].setStroke(Color.BLACK);
this.field[i][j].setFill(Color.LIGHTGRAY);
hbox.getChildren().add(this.field[i][j]);
}
vbox.getChildren().add(hbox);
}
return vbox;
}
This is the result:
I need to receive the Index of a Rectangle when the user clicks one of it.
Can you guys give me an example / code snipped how to accomplish my problem?
I recommend using a GridPane instead of HBoxes and a VBox. You could copy the loop indices to final local variables to access them from a anonymus EventHandler class/lambda expression created inside the loop body or you could use GridPane.getRowIndex/GridPane.getColumnIndex on the GridPane children:
public GridPane CreateField(){
GridPane grid = new GridPane();
grid.setVgap(2);
grid.setHgap(2);
EventHandler<MouseEvent> handler = evt -> {
Node source = (Node) evt.getSource();
int row = GridPane.getRowIndex(source);
int column = GridPane.getColumnIndex(source);
...
};
for(int i = 0; i < this.columns; i++){
for(int j = 0; j < this.rows; j++){
Rectangle rect = new Rectangle(this.height*j, this.width*i, this.height, this.width);
this.array[j][i] = 0;
this.field[i][j] = rect;
rect.setStroke(Color.BLACK);
rect.setFill(Color.LIGHTGRAY);
rect.setOnMouseClicked(handler);
grid.add(rect, j, i);
}
}
return grid;
}
You could also use
final int finalI = i;
final int finalJ = j;
rect.setOnMouseClicked(evt -> {
// TODO: use finalI and finalJ here
});
in the inner loop instead.
Lol, got it faster work than I thought. Here is my solution (How can I get the indexes of each button clicked for my program?)
private EventHandler<? super MouseEvent> createTileHandler(int x, int y) {
return event -> tileHandler(x, y);
}
private void tileHandler (int x, int y){
System.out.println(String.format("Clicked tile at (%d,%d)", x, y));
}
public VBox CreateField(){
VBox vbox = new VBox(2);
for(int i = 0; i < this.columns; ++i){
HBox hbox = new HBox(2);
for(int j = 0; j < this.rows; ++j){
this.array[j][i] = 0;
this.field[i][j] = new Rectangle(this.height*j, this.width*i, this.height, this.width);
this.field[i][j].setStroke(Color.BLACK);
this.field[i][j].setFill(Color.LIGHTGRAY);
this.field[i][j].setOnMouseClicked(createTileHandler(i,j));
hbox.getChildren().add(this.field[i][j]);
}
vbox.getChildren().add(hbox);
}
return vbox;
}
This returns:
Clicked tile at (3,2)
Clicked tile at (3,2)
Clicked tile at (4,4)
Clicked tile at (3,0)
Clicked tile at (8,8)
Clicked tile at (9,9)
Clicked tile at (0,0)
Clicked tile at (0,1)
Clicked tile at (0,2)
When I click one Shape.
Apologizing for this questions ..
I'm trying to implement a chess board with draggable pieces as seen below. However, I'm unable to keep the piece being dragged to stay in front of other nodes to below or right to it. Left and up seems to work fine.
I tried to solve this by declaring the StackPanes forming the checkered background first and all the pieces only after that, as I read Java assigns the z-index based on the order in which the Nodes are added to their Parents. This approach is reflected below. I also tried creating a Group and adding both StackPanes and ImageViews to it in order to be able to use toFront(). Resulted in only the coordinate labels being shown.
How can I achieve the functionality I'm after?
This method creates the board:
public Parent chessBoard() {
GridPane board = new GridPane();
StackPane[][] cells = new StackPane[8][8];
// Create the board first
// (For dragging pieces to work correctly, draggable pieces must be
// added after the whole board, since z-index cannot be set explicitly
// in JavaFX.
for (int row = 0; row < 10; row++) {
for (int col = 0; col < 10; col++) {
// x and y in chess coordinate system (0-indexed)
int[] invertedY = {-1,7,6,5,4,3,2,1,0,-1};
int x = col - 1;
int y = invertedY[row];
// Coordinate labels
String[] abcLabels = {"A","B","C","D","E","F","G","H"};
if (row == 9 || row == 0) {
if (col == 0 || col == 9) continue;
Label label = new Label(abcLabels[x]);
label.setTextAlignment(TextAlignment.CENTER);
board.add(label, col, row);
continue;
} else if (col == 0 || col == 9) {
Label label = new Label(Integer.toString(y + 1));
board.add(label, col, row);
continue;
}
// Cell background color
Square square = game.getBoard().getSquare(x, y);
Color color = square.getColor() == ChessColor.BLACK
? Color.PERU : Color.BLANCHEDALMOND;
StackPane cell = cells[y][x] = new StackPane();
cell.setMaxSize(60, 60);
cell.setMinSize(60, 60);
cell.setBackground(new Background(
new BackgroundFill(color, null, null)));
board.add(cell, col, row);
}
}
// Finally, add pieces to their respective cells
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
Square square = game.getBoard().getSquare(x, y);
Piece occupant = square.getOccupant();
if (occupant != null) {
String path = "/resources/" + occupant + ".png";
Image image =
new Image(getClass().getResourceAsStream(path));
DraggablePieceIcon imageView =
new DraggablePieceIcon(image);
imageView.setManaged(false);
cells[y][x].getChildren().add(imageView);
}
}
}
return board;
}
This class makes the draggable icons:
public class DraggablePieceIcon extends ImageView {
private double mouseX;
private double mouseY;
public DraggablePieceIcon(Image image) {
super(image);
setOnMousePressed(event -> {
mouseX = event.getSceneX();
mouseY = event.getSceneY();
});
setOnMouseDragged(event -> {
double deltaX = event.getSceneX() - mouseX;
double deltaY = event.getSceneY() - mouseY;
relocate(getLayoutX() + deltaX, getLayoutY() + deltaY);
mouseX = event.getSceneX();
mouseY = event.getSceneY();
});
}
}
And here's what I'm seeing:
You're adding the cells row by row from left to right. Since you add the pieces to the cells, the descendants of a cell cover the contents of cells in rows above or in the same row and left to the cell and will be covered by contents of all other cells.
To fix this you could make the parent of a dragged item the topmost node in the GridPane:
public DraggablePieceIcon(Image image) {
super(image);
setOnMousePressed(event -> {
mouseX = event.getSceneX();
mouseY = event.getSceneY();
// make cell containing this piece the top-most cell
this.getParent().toFront()
});
...
}
Note that this solution will require you to implement some logic that makes the pieces children of the cells they are moved to and move the pieces to the center of those cells. Otherwise a piece could be covered by other cells, if you drag the piece inside such a cell later...
An alternative would be to make the pieces children of the GridPane itself. You allow the pieces to be dragged around independent of the cells anyways; the association between cell and piece is important for the model (i.e. in this case the implementation of the chess rules) not for the view and usually these parts are kept seperate.
I have AnchorPane which is the Main that has all other panes.
Inside it I have the board area which is AnchorPane and area for the dice which is also AnchorPane.
This is how it looks on scene builder:
inside the board Area which is AnchorPane I am creating dynamically (according to the user request) the size of the board (5x5, 6x6, 7x7 or 8x8):
GridPane boardGame;
#FXML
Button dice;
#FXML
AnchorPane boardArea;
#FXML
AnchorPane boardGameAnchorPane;
#FXML
AnchorPane dicePane;
public void CreateBoard()
{
int boardSize = m_Engine.GetBoard().GetBoardSize();
int num = 1;
int maxColumns = m_Engine.GetNumOfCols();
int maxRows = m_Engine.GetNumOfRows();
boardGame = new GridPane();
//boardGame.setAlignment(Pos.CENTER);
for(int row = maxRows - 1; row >= 0 ; row--)
{
for(int col = 0; col < maxColumns ; col++)
{
StackPane stackPane = new StackPane();
stackPane.setMaxSize(SIZE_OF_CELL, SIZE_OF_CELL);
stackPane.setMinSize(SIZE_OF_CELL, SIZE_OF_CELL);
if((col + row) % 2 != 0)
{
stackPane.getStyleClass().add("oddCellBorder");
}
else
{
stackPane.getStyleClass().add("evenCellBorder");
}
Label label = new Label(String.valueOf(num));
StackPane.setAlignment(label, Pos.BOTTOM_LEFT);
stackPane.getChildren().add(label);
boardGame.add(stackPane, col, row);
num++;
}
}
this.fixBoardGameSize();
boardGame.setGridLinesVisible(true);
// boardGame.autosize();
boardArea.getChildren().add(boardGame);
//ImageView imageView = ImageUtils.getImageView("diceTransprntBack3D.png");
//dice.setGraphic(imageView);
// Image img1 = new Image(getClass().getResourceAsStream("about.png"));
}
My problem starts from board size of 7x7 or 8x8.
The board area spread to the dice area:
I tried to fix the sizes manually with this function:
private void fixBoardGameSize()
{
boardArea.setMinHeight(boardGame.getHeight());
boardArea.setMaxHeight(boardGame.getHeight());
boardArea.setMinWidth(boardGame.getWidth());
boardArea.setMaxWidth(boardGame.getWidth());
boardGameAnchorPane.setPrefWidth(boardArea.getWidth() + dicePane.getWidth() + 500 );
boardGameAnchorPane.setMinWidth(boardArea.getWidth() + dicePane.getWidth() + 500 );
boardGameAnchorPane.setMaxWidth(boardArea.getWidth() + dicePane.getWidth() + 500 );
dicePane.setLayoutX(boardArea.getLayoutX() + boardArea.getWidth() + 1000);
}
But with no success.
I tried also to play with the anchor pain constraint with no success.
I searched but can't find something that can help me so I am asking here if someone knows how I can keep the two anchor panes separated.
Based on your description, using the same anchor panes, and given a fixed size of your application, what you can do is:
First, set a maximum size for the boardArea, a margin for the grid to be correctly displayed on that pane
// Maximum fixed size you give to boardArea
public static final double MAX_SIZE = 600;
// Margin to border
public static final double MARGIN = 25;
And now, in your fixBoardGameSize() method, resize properly the grid:
private void fixBoardGameSize(){
// Listener, since boardGame dimensions are determined after the stage is shown
ChangeListener<Number> resize = (ov, v, v1) -> {
double scale = Math.min((MAX_SIZE - 2d*MARGIN) / boardGame.getWidth(),
(MAX_SIZE - 2d*MARGIN) / boardGame.getHeight());
boardGame.setScaleX(scale);
boardGame.setScaleY(scale);
boardGame.setTranslateX((MAX_SIZE - boardGame.getWidth()) / 2d);
boardGame.setTranslateY((MAX_SIZE - boardGame.getHeight()) / 2d);
};
boardGame.widthProperty().addListener(resize);
boardGame.heightProperty().addListener(resize);
}
Now you can display games with any number of rows and columns. It will always fit in the board, and it will be properly centered.