Java-FX Receive Index of Shape-Array when clicked - java

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 ..

Related

Grid with Buttons

I need to print out a grid in a windows and I need button under the grid. So that I can control the action on the grid, but at the moment I don't get the grid printed out only the buttons
// Create and initialize cell
public Cell[][] cell = new Cell[rows][cols];
protected BorderPane getPane() {
HBox paneForButtons = new HBox(20);
//button start
startB.setOnAction(e -> {
if( startB.isPressed() == false){
System.out.println("start");
}
});
//button stop
stopB.setOnAction(e -> {
if( stopB.isPressed() == false){
System.out.println("stop");
}
});
//button left
leftB.setOnAction(e -> text.getTransforms().add(new Rotate(-rotate, text.getX(), text.getY())));
//button right
rightB.setOnAction(e -> text.getTransforms().add(new Rotate(rotate, text.getX(), text.getY())));
//fiel to get the moves
moveFeld.setPromptText("1,2 or 3 steps");
Button absenden = new Button();
absenden.setOnAction(e -> {
ReadInput2();
if( moveFeld.getText() != null && !moveFeld.getText().isEmpty()){
System.out.println("Moves are " + moveFeld.getText().toString());
}
});
absenden.setText("enter");
paneForButtons.getChildren().addAll(startB, stopB, leftB, rightB, moveFeld, absenden);
paneForButtons.setAlignment(Pos.CENTER);
paneForButtons.setStyle("-fx-border-color: green");
BorderPane pane = new BorderPane();
pane.setBottom(paneForButtons);
//pane.setBottom(paneForButtons);
// Pane to hold cell
GridPane pane2 = new GridPane();
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
pane2.add(cell[i][j] = new Cell(), j, i);
pane2.setAlignment(Pos.CENTER);
pane.setCenter(pane2);
return pane;
}
public static class Cell extends Pane {
public Cell() {
setStyle("-fx-border-color: black");
this.setPrefSize(800, 800);
}
}
I'm calling the method getPane in my void so I think there is not problem with that you guys have any idea how I get the grid above my buttons.
Now to part two the grid can be painted however the user wants I putted in 5 for now but in the future it should be mutable

How to draw a grid and paint the grid corners with a point on JavaFX?

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();
}
}

JavaFX creating a game board with GridPane

When the application is run the game board shows fine, although when a tile is clicked the text always shows in the top left corner of the board [0][0].
Also I am struggling with the logic. I have a int board matrix with a function makeMove which to add a players move to the matrix, although I can't get the player to make a move on the matrix and also print a corresponding "O" on the tile index.
How would I create a function that can be passed a row, col index and alter the board matrix and print the player piece on the GUI (e.g.for AI move)
And also when the GUI tile is clicked by the player to then make the corresponding move.
So far I have created a GridPane and added Rectangle with text at each index for the GUI.
I have created a Board class which constructs an int board matrix to hold player moves (P1, P2, empty).
public class Board {
private int[][] board_matrix;
private int board_size;
private int win_length;
public Board(int board_size, int win_length) {
this.board_matrix = new int[board_size][board_size];
this.board_size = board_size;
this.win_length = win_length;
for (int i = 0; i < board_size; i++) {
for (int j = 0; j < board_size; j++) {
this.board_matrix[i][j] = 0;
}
}
}
public void make_move(int player, int x_pos, int y_pos) {
if (player == 1) board_matrix[x_pos][y_pos] = 1;
else board_matrix[x_pos][y_pos] = 2;
}
public class BoardGUI_ extends Application {
private final int BOARD_SIZE = 15;
public Parent createBoard() {
GridPane gameBoard = new GridPane();
gameBoard.setPrefSize(755, 755);
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
Rectangle tile = new Rectangle(50, 50);
tile.setFill(Color.BURLYWOOD);
tile.setStroke(Color.BLACK);
Text text = new Text();
text.setFont(Font.font(40));
GridPane.setRowIndex(tile, i);
GridPane.setColumnIndex(tile, j);
tile.setOnMouseClicked(event -> drawMove(text));
gameBoard.getChildren().addAll(tile, text);
}
}
return gameBoard;
}
public void drawMove(Text text) {
text.setText("O");
text.setFill(Color.BLACK);
}
If you want to add a Text object on top of a Rectangle object wrap both in a StackPane:
public Parent createBoard() {
GridPane gameBoard = new GridPane();
gameBoard.setPrefSize(755, 755);
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
Rectangle tile = new Rectangle(50, 50);
tile.setFill(Color.BURLYWOOD);
tile.setStroke(Color.BLACK);
Text text = new Text();
text.setFont(Font.font(40));
gameBoard.add(new StackPane(tile, text), j, i);
//GridPane.setRowIndex(tile, i);
//GridPane.setColumnIndex(tile, j);
//gameBoard.getChildren().addAll(tile, text);
tile.setOnMouseClicked(event -> drawMove(text));
}
}
return gameBoard;
}
In your loop you are setting the row and column of the 'tile' node, but not the 'text' node. So you have all of your text nodes at 0,0.
It seems you want the tiles to be StackPanes as c0der suggests. With the Text node on top of the tile.
I think makes sense to create a Tile object, possibly derived from StackPane, and use it to manage the each tile. It would be responsible for the background color and text shown. It's not clear at this point if the Rectangle is even needed. You could just set the background and border of the StackPane.

How to keep dragged Node in front of others (JavaFX 8)?

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.

TableView and List Data

I am trying to display a List of icons (which are referred simply by their path) in a TableView that has x-amount of columns and any number of cells that are required to display all the icons.
The plan was to display the icons in such a way that the TableView acts as a "multi-lined" ListView.. so that they go from left to the right.
It's the first time I'm using the TableView control, and I'm a bit confused of how to achieve this.
Thanks for any pointers.
So, as ItachiUchiha suggested, I ended up using a Pagination control with a GridPane, and it works very nicely.
Here's the code if anyone stumbles upon the same thing..
List<String> allEntries = Icons.getAllEntries();
int numCols = 8;
int numRows = 5;
int numPages = allEntries.size() / (numCols * numRows);
Pagination pagination = new Pagination(numPages);
Collections.sort(allEntries);
pagination.setPageFactory(pageIndex -> {
GridPane layout = new GridPane();
for (int y = 0; y < numRows; y++) {
for (int x = 0; x < numCols; x++) {
int index = numCols * y + x + (pageIndex * numCols * numRows);
String path = allEntries.get(index);
Image image = new Image(path, true);
ImageView imageView = new ImageView(image);
imageView.setFitWidth(64);
imageView.setFitHeight(64);
layout.add(imageView, x, y);
imageView.setOnMousePressed(e -> {
// Do stuff
});
}
}
return layout;
});

Categories