How do I adjust the Pane to the windows size? So that when I change the window size the nodes also adjust to this situation.
Pane root = new Pane();
for (int i = 1; i <= 5; i++)
{
Button b = new Button("Button " + i);
b.setLayoutX((i*100));
}
Related
I have code generated gridpane. I generates columns and rows based on data it gets from the server. I append a VBox that contains Label (on top) and ImageView (on bottom) to every cell. Each VBox is different. I look at each VBox as container. I would like to make each VBox selectable in radiobutton behavior - only one in the gridpane can be selected. This is my code for generating gridpane and generating VBoxes and appending them to cells in gridpane:
private void vBoxPickPane(List<Data> data){
int columns = 3;
int vBoxSpacing = 2;
int cellWidth = 260;
int cellHeight = 220;
List<Label> labels = new ArrayList<Label>();
List<ImageView> imageViews = new ArrayList<>();
List<VBox> vBoxes = new ArrayList<>();
for(int l=0; l != data.size(); l++){
labels.add(new Label(data.get(l).getName()));
imageViews.add(new ImageView(new Image(new File(data.get(l).getImgFileUrl()).toURI().toString())));
}
int labelIndex = 0;
for(int u=0;u!=columns;u++){
gridPane.getColumnConstraints().add(new ColumnConstraints(cellWidth));
}
for(int i=0; i <= data.size()/columns; i++){
gridPane.getRowConstraints().add(new RowConstraints(cellHeight));
}
for(int i=0; i<data.size();i++){
vBoxes.add(new VBox(vBoxSpacing));
GridPane.setConstraints(vBoxes.get(i), i%columns, i/columns);
}
for(int o=0; o != imageViews.size(); o++){
imageViews.get(o).setFitHeight(200);
imageViews.get(o).setFitWidth(255);
}
for(int k = 0; k != labels.size(); k++){
labels.get(k).setStyle("-fx-background-color:white;");
}
for(int u = 0; u != vBoxes.size(); u++){
vBoxes.get(u).getChildren().addAll(labels.get(u), imageViews.get(u));
}
gridPane.getChildren().addAll(vBoxes);
}
The code requires a clean up which I'll do after I figure out how to make each cell clickable in radiobutton behavior.
I was thinking that maybe I should create VBox variable for selected one and then check if it already has value and if it does have value and another VBox is clicked then it would change to the one that is clicked. But I also have to let the user know that he selected one vbox and change it's background color or something similar. Also I don't know how I would create each cell as clickable.
I did it exactly like I thought I would.
for(int u = 0; u != vBoxes.size(); u++){
final VBox curr = vBoxes.get(u);
curr.getChildren().addAll(labels.get(u), imageViews.get(u));
curr.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if(selectedVbox != null){
//odselektaj
selectedVbox.setStyle("-fx-background-color: #272D2D;");
}
selectedVbox = curr;
selectedVbox.setStyle("-fx-background-color: #e74c3c;");
}
});
}
I did an override on handle method with setting click listener on each vboxes and then checking if one is already clicked and if not changed it's background and if it already was then I overwritten the variable.
I have a class which extends StackPane. Another one which makes a 20x20 2d Array of this class. I then translated their position somewhere on the scene. Right now, I can't obtain the position of a cell relative on the scene.
I tried Bounds, Point2D.zero and getMin..
cell.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
System.out.println("mouse click detected! " + mouseEvent.getSource());
// boundsInScene = cell.localToScene(cell.getLayoutBounds());
// System.out.println("Cell width: " + boundsInScene.getMinX());
// System.out.println("Cell height: " + boundsInScene.getMinY());
// cell.localToScene(Point2D.ZERO);
int x = (int) cell.getTranslateX();
int y = (int) cell.getTranslateY();
System.out.println("X: " + x + "y :" + y);
}
});
Part of my code:
private Parent createContent() {
root = new Pane();
root.setPrefSize(640, 480);
for (int col = 0; col < y_Tiles; col++) {
for (int row = 0; row < x_Tiles; row++) {
cell = new Cell(row, col);
grid_Array[row][col] = cell;
cell.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {});
root.getChildren().add(cell);
}
}
return root;
}
Bounds is resturning me 480 and 400. Another thing is that all cells return the same output for any of the methods I tried. Any idea how to cell the position of a cell on the scene?
PS:Sorry for bad english. Not native language
You shouldn't be using translateX/Y to set the location of your items. translateX/Y should just be used for temporary movements of an item, such as a bounce effect or a transition. layoutX/Y should be used to set the location of items. Normally layoutX/Y will be set automatically by a layout pane. If you aren't using a layout pane and are manually performing the layout in an unmanaged parent (such as a Pane or a Group), then you can manually set the layoutX/Y values as needed (relocate will do this).
Once you switch to setting the layout values rather than the translate values, then you determine the location of any node in a scene using the localToScene function.
Bounds boundsInScene = node.localToScene(node.getLayoutBounds());
I posted a question here a couple weeks ago for some help on making a chess game and I got a great response and some code that demonstrates a solution to my problem. I have been trying to dissect the code and study it so I can better understand how it works. While doing this I ran into a question I can not seem to find an answer for. The chess board is made up of a 2d array of Regions and I am trying to add a MouseListener to each Region in the 2d array so that when a Region is pressed it will print out the row and column of the Region that was pressed. Right now nothing is happening when I press on a square in my screen and I can't not figure out why my MouseListener is not working.
public class Main extends Application {
GridPane root = new GridPane();
final int size = 8;
int col = 0;
int row = 0;
#Override
public void start(Stage primaryStage) {
try {
GridPane board = new GridPane();
Region[][] fields = new Region[8][8];
for (int i = 0; i < fields.length; i++) {
Region[] flds = fields[i];
for (int j = 0; j < flds.length; j++) {
Region field = new Region();
flds[j] = field;
field.setBackground(new Background(new BackgroundFill((i + j) % 2 == 0 ? Color.WHITE : Color.LIGHTBLUE, CornerRadii.EMPTY, Insets.EMPTY)));
}
board.addRow(i, flds);
}
for (int i = 0; i < fields.length; i++) {
col = i;
for (int j = 0; j < fields[i].length; j++) {
row = j;
fields[i][j].setOnMouseClicked(e->{
System.out.println("Col:" + col + ", Row" + row);
});
}
}
// use 1/8 of the size of the Grid for each field
RowConstraints rowConstraints = new RowConstraints();
rowConstraints.setPercentHeight(100d / 8);
ColumnConstraints columnConstraints = new ColumnConstraints();
columnConstraints.setPercentWidth(100d / 8);
for (int i = 0; i < 8; i++) {
board.getColumnConstraints().add(columnConstraints);
board.getRowConstraints().add(rowConstraints);
}
Pane piecePane = new Pane();
StackPane root = new StackPane(board, piecePane);
// NumberBinding boardSize = Bindings.min(root.widthProperty(), root.heightProperty());
NumberBinding boardSize = Bindings.min(root.widthProperty(), root.heightProperty());
// board size should be as large as possible but at most the min of the parent sizes
board.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
// same size for piecePane
piecePane.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
piecePane.maxWidthProperty().bind(boardSize);
piecePane.maxHeightProperty().bind(boardSize);
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Change:
StackPane root = new StackPane(board,piecePane);
to:
StackPane root = new StackPane(piecePane,board);
🐞?
Because the board was behind the piecePane it couldn't receive events.
Before you were using global variables col and row so these variables had always the same value of 7 and 7. Running the loop those variables where changing their value,but at the end they had the values 7 7 , here we need to use local variables col and row , so you need to add this modification:
for (int i = 0; i < fields.length; i++) {
int col = i;
for (int j = 0; j < fields[i].length; j++) {
int row = j;
fields[i][j].setOnMouseClicked(e -> System.out.println("Col:" + col + ", Row" + row));
}
}
I haven't worked much in JavaFX myself, though it is very similar to java swing regarding basic "collision".
There is a rectangle class with the methods contains and intersects. If you use MouseEvent to know the mouse coordinates and when the mouse is clicked. You can do something like this:
if(mouse.isClicked() && rect.contains(mouse.x, mouse.y) { // do stuff }
You need to create rectangle objects for all the squares though. Hope it works out.
EDIT: Noticed Regions also contained the methods contains and intersects - try these beforehand, use rectangles only if you must to skip too much object creation.
I have a chess board and I am trying to add pieces to the board. Every spot on the board is a Rectangle so I thought the best way to add pieces would be to add an ImagePattern to each Rectangle that gets a piece. The problem I encountered was when I added an ImagePattern to a Rectangle it would make the background of that Rectangle white despite what the color was before the ImagePattern was added. So my question is, is there a way for me to preserve the background color of a Rectangle after an ImagePattern is added?
For demo purposes my code only adds one piece to the board.
public class ChessBoard 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);
if(row == 4 && col == 3){
Image p = new Image("Peices/Black/0.png");
ImagePattern pat = new ImagePattern(p);
square.setFill(pat);
}
square.widthProperty().bind(root.widthProperty().divide(size));
square.heightProperty().bind(root.heightProperty().divide(size));
square.setOnMouseClicked(e->{
square.setFill(Color.BLUE);
});
}
}
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I think you are searching for blending which is done with the BlendMode. For example:
Image p = new Image("Peices/Black/0.png");
ImagePattern pat = new ImagePattern(p);
Rectangle r1 = new Rectangle();
r1.setX(50);
r1.setY(50);
r1.setWidth(50);
r1.setHeight(50);
r1.setFill(pat);
Rectangle r = new Rectangle();
r.setX(50);
r.setY(50);
r.setWidth(50);
r.setHeight(50);
r.setFill(Color.BLUE);
r.setBlendMode(BlendMode.ADD);
As far as I know there is no direct way to accomplish this.
Source: https://docs.oracle.com/javase/8/javafx/api/javafx/scene/effect/BlendMode.html
No, you cannot use more than a single fill with a Rectangle. Theoretically you could use a Region with multiple backgrounds, but this is probably a bad idea. Most likely you'll want at least some of the following functionality for the pieces, which will not work, if you do not make pieces their own nodes:
Dragging a piece from one field to another
animating moves
I recommend using a StackPane and put the Board in the background and put a Pane on top of it to place the pieces. Simply use ImageViews for the pieces.
Example:
#Override
public void start(Stage primaryStage) {
GridPane board = new GridPane();
Region[][] fields = new Region[8][8];
for (int i = 0; i < fields.length; i++) {
Region[] flds = fields[i];
for (int j = 0; j < flds.length; j++) {
Region field = new Region();
flds[j] = field;
field.setBackground(new Background(new BackgroundFill((i + j) % 2 == 0 ? Color.WHITE : Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY)));
}
board.addRow(i, flds);
}
// use 1/8 of the size of the Grid for each field
RowConstraints rowConstraints = new RowConstraints();
rowConstraints.setPercentHeight(100d / 8);
ColumnConstraints columnConstraints = new ColumnConstraints();
columnConstraints.setPercentWidth(100d / 8);
for (int i = 0; i < 8; i++) {
board.getColumnConstraints().add(columnConstraints);
board.getRowConstraints().add(rowConstraints);
}
Pane piecePane = new Pane();
StackPane root = new StackPane(board, piecePane);
NumberBinding boardSize = Bindings.min(root.widthProperty(), root.heightProperty());
ImageView queen = new ImageView("https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/Chess_qdt45.svg/480px-Chess_qdt45.svg.png");
DropShadow shadow = new DropShadow(BlurType.GAUSSIAN, Color.WHITE, 2, 1, 0, 0);
// drop shadow for black piece on black field
queen.setEffect(shadow);
// trigger move to topleft field on mouse click
queen.setOnMouseClicked(evt -> {
Node source = (Node) evt.getSource();
TranslateTransition animation = new TranslateTransition(Duration.seconds(0.5), source);
Region targetRegion = fields[0][0];
final PositionChangeListener listener = (PositionChangeListener) source.getUserData();
listener.setField(null);
animation.setByX(targetRegion.getLayoutX() - source.getLayoutX());
animation.setByY(targetRegion.getLayoutY() - source.getLayoutY());
animation.setOnFinished(e -> {
source.setTranslateX(0);
source.setTranslateY(0);
listener.setField(targetRegion);
});
animation.play();
});
PositionChangeListener changeListener = new PositionChangeListener(queen);
queen.setUserData(changeListener);
changeListener.setField(fields[4][3]);
// board size should be as large as possible but at most the min of the parent sizes
board.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
board.maxWidthProperty().bind(boardSize);
board.maxHeightProperty().bind(boardSize);
// same size for piecePane
piecePane.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
piecePane.maxWidthProperty().bind(boardSize);
piecePane.maxHeightProperty().bind(boardSize);
// add piece to piecePane
piecePane.getChildren().add(queen);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private static class PositionChangeListener implements ChangeListener<Bounds> {
private final ImageView piece;
public PositionChangeListener(ImageView piece) {
this.piece = piece;
}
private Region currentField;
public void setField(Region newRegion) {
// register/unregister listeners to bounds changes of associated field
if (currentField != null) {
currentField.boundsInParentProperty().removeListener(this);
}
currentField = newRegion;
if (newRegion != null) {
newRegion.boundsInParentProperty().addListener(this);
changed(null, null, newRegion.getBoundsInParent());
}
}
#Override
public void changed(ObservableValue<? extends Bounds> observable, Bounds oldValue, Bounds newValue) {
// align piece with field
piece.setLayoutX(newValue.getMinX());
piece.setLayoutY(newValue.getMinY());
piece.setFitHeight(newValue.getHeight());
piece.setFitWidth(newValue.getWidth());
}
}
I'm trying to combine 3 features into a single JavaFX class. My first feature displays "WELCOME TO JAVA" around in a circle. The second displays a 10x10 matrix of random 1's and 0's. The third displays a smiley face. They should be displayed one after the other in a single pane. I have the first and third features but the matrix one is throwing me off. Although everything is supposed to be in a single pane to display on the same GUI window (per professor), I don't see how else I could've done the matrix other than creating the gridPane. It displays fine without the size constraints, but then it takes up the entire screen and my other 2 features aren't visible. When I add the constraints, it gets small and the numbers aren't visible. I'm not sure how I can fix this. Can someone please help?
Pane pane = new Pane();
// Create a circle and set its properties
Circle circle = new Circle();
circle.setCenterX(100);
circle.setCenterY(100);
circle.setRadius(50);
circle.setStroke(null);
circle.setFill(null);
pane.getChildren().add(circle); // Add circle to the pane
//Display WELCOME TO JAVA with the text forming a circle
int i = 0;
String phrase = "WELCOME TO JAVA ";
double degree = 360 / phrase.length();
for (double degrees = 0; i < phrase.length(); i++, degrees += degree) {
double pointX = circle.getCenterX() + circle.getRadius() *
Math.cos(Math.toRadians(degrees));
double pointY = circle.getCenterY() + circle.getRadius() *
Math.sin(Math.toRadians(degrees));
Text letter = new Text(pointX, pointY, phrase.charAt(i) + "");
letter.setFill(Color.BLACK);
letter.setFont(Font.font("Times New Roman", FontWeight.BOLD, 20));
letter.setRotate(degrees + 90);
pane.getChildren().add(letter); }
//Create a 10x10 matrix of 1s and 0s
GridPane pane2 = new GridPane();
pane2.setHgap(1);
pane2.setVgap(1);
Button[][] matrix;
int length = 10;
int width = 10;
ArrayList<TextField> textFields = new ArrayList<>();
for (int y = 0; y < length; y++) {
ColumnConstraints colConst = new ColumnConstraints();
colConst.setPercentWidth(10);
pane2.getColumnConstraints().add(colConst);
for (int x = 0; x < width; x++) {
RowConstraints rowConst = new RowConstraints();
rowConst.setPercentHeight(10);
pane2.getRowConstraints().add(rowConst);
Random rand = new Random();
int random1 = rand.nextInt(2);
TextField textf = new TextField();
textf.setText("" + random1);
textf.setPrefSize(15, 15);
pane2.setRowIndex(textf, x);
pane2.setColumnIndex(textf, y);
pane2.getChildren().add(textf);
}}
//Create a smiley face
Circle circle2 = new Circle();
circle2.setCenterX(600.0f);
circle2.setCenterY(100.0f);
circle2.setRadius(50.0f);
circle2.setStroke(Color.BLACK);
circle2.setFill(null);
pane.getChildren().add(circle2);
Circle leftInnerEye = new Circle();
leftInnerEye.setCenterX(580.0f);
leftInnerEye.setCenterY(85.0f);
leftInnerEye.setRadius(5);
leftInnerEye.setStroke(Color.BLACK);
pane.getChildren().add(leftInnerEye);
Ellipse leftOutterEye = new Ellipse();
leftOutterEye.setCenterX(580.0f);
leftOutterEye.setCenterY(85.0f);
leftOutterEye.setRadiusX(11.0f);
leftOutterEye.setRadiusY(8.0f);
leftOutterEye.setStroke(Color.BLACK);
leftOutterEye.setFill(null);
pane.getChildren().add(leftOutterEye);
Circle rightEye = new Circle();
rightEye.setCenterX(620.0f);
rightEye.setCenterY(85.0f);
rightEye.setRadius(5);
rightEye.setStroke(Color.BLACK);
pane.getChildren().add(rightEye);
Ellipse rightOutterEye = new Ellipse();
rightOutterEye.setCenterX(620.0f);
rightOutterEye.setCenterY(85.0f);
rightOutterEye.setRadiusX(11.0f);
rightOutterEye.setRadiusY(8.0f);
rightOutterEye.setStroke(Color.BLACK);
rightOutterEye.setFill(null);
pane.getChildren().add(rightOutterEye);
Polygon nose = new Polygon();
nose.getPoints().setAll(
600d, 90d,
588d, 115d,
612d, 115d );
nose.setStroke(Color.BLACK);
nose.setFill(null);
pane.getChildren().add(nose);
Arc mouth = new Arc(600, 115, 30, 16, 180, 180);
mouth.setFill(null);
mouth.setType(ArcType.OPEN);
mouth.setStroke(Color.BLACK);
pane.getChildren().add(mouth);
HBox hbox = new HBox(pane, pane2);
hbox.autosize();
hbox.setAlignment(Pos.BASELINE_LEFT);
hbox.setPadding(new Insets(20));
// Create a scene and place it in the stage
Scene scene = new Scene(hbox, 1000, 500);
primaryStage.setTitle("Laura's Chapter 14"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
}
catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
Create a pane for each feature and then add the features to either a VBox or HBox, that will root pane of the scene. That way you can have a GridPane for your second feature. There are different layouts available in JavaFX and they behave differently. Take a look at this documentation and its sub documentations.