Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I'm trying to make a Scrabble game, but I'm having trouble adding the tiles to match exactly to the board coordinates. SO far I'm having trouble figuring that out. Here is my code
import javafx.application.Application;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Main extends Application {
public static final int TILE_SIZE = 40;
private static final int W = 800;
private static final int H = 600;
private static final int X_TILES = 15;
private static final int Y_TILES = 15;
private Tile[][] grid = new Tile[X_TILES][Y_TILES];
private GameTile[][] bag = new GameTile[19][19];
double orgSceneX, orgSceneY;
double orgTranslateX, orgTranslateY;
Group tileGroup = new Group();
Group pieceGroup = new Group();
private Scene scene;
static public void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
scene = new Scene(createContent());
stage.setScene(scene);
stage.show();
}
private Parent createContent() {
Pane root = new Pane();
root.setPrefSize(W, H);
root.getChildren().addAll(tileGroup, pieceGroup);
//creates board tiles
for (int x = 0; x < X_TILES; x++) {
for (int y = 0; y < Y_TILES; y++) {
Tile tile = new Tile(x, y);
grid[y][x] = tile;
tileGroup.getChildren().add(tile);
}
}
int z = 0;
//creates bag tiles
for (int y = 0; y < 3; y++) {
for (int x = 15; x < 18; x++) {
GameTile bagTile = new GameTile(x, y);
bagTile.setCursor(Cursor.HAND);
bagTile.setOnMousePressed(e -> {
orgSceneX = e.getSceneX();
orgSceneY = e.getSceneY();
orgTranslateX = ((GameTile) (e.getSource())).getTranslateX();
orgTranslateY = ((GameTile) (e.getSource())).getTranslateY();
});
bagTile.setOnMouseDragged(e -> {
double offsetX = e.getSceneX() - orgSceneX;
double offsetY = e.getSceneY() - orgSceneY;
double newTranslateX = orgTranslateX + offsetX;
double newTranslateY = orgTranslateY + offsetY;
((GameTile) (e.getSource())).setTranslateX((int)(newTranslateX));
((GameTile) (e.getSource())).setTranslateY((int)newTranslateY);
});
bagTile.setOnMouseReleased(e -> {
});
bag[x][y] = bagTile;
root.getChildren().add(bagTile);
z++;
}
}
return root;
}
}
I tried implementing a mouse handler, but so far I keep failing to understand how to make the exact coordinates match the board underneath the draggable tiles.
One way to do this is to use ideas from this answer. I altered the code so that it sets the node that is being dragged invisible and shows a snapshot of that node being dragged by the mouse.
You could probably use Label instead of Button for the Tiles.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.Button;
import javafx.scene.input.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
* Demonstrates a drag-and-drop feature.
*/
public class App extends Application
{
#Override
public void start(Stage stage)
{
//Source Buttons.
final Button boat1 = new Button("boat1");
final Button boat2 = new Button("boat2");
final Button boat3 = new Button("boat3");
final Button boat4 = new Button("boat4");
//Adding OnDragDetected to source Buttons.
setOnDragDetected(boat1);
setOnDragDetected(boat2);
setOnDragDetected(boat3);
setOnDragDetected(boat4);
//Adding onDragDone to source Buttons.
setOnDragDone(boat1);
setOnDragDone(boat2);
setOnDragDone(boat3);
setOnDragDone(boat4);
//Creating GridPane
GridPane gridPane = new GridPane();
gridPane.setVgap(5);
gridPane.setHgap(5);
gridPane.setPadding(new Insets(5, 5, 5, 5));
gridPane.setStyle("-fx-background-color: black;");
//Adding StackPane to every Cell in the GridPane and Adding the Target Events to each StackPane.
for (int i = 0; i < 6; i++) {
StackPane stackPane = new StackPane();
stackPane.setPrefSize(150, 50);
stackPane.setStyle("-fx-background-color: yellow;");
setOnDragOver(stackPane);
setOnDragEntered(stackPane);
setOnDragExited(stackPane);
setOnDragDropped(stackPane);
gridPane.add(stackPane, i / 3, i % 3);
}
HBox root = new HBox(new VBox(boat1, boat2, boat3, boat4), gridPane);
stage.setTitle("Hello Drag And Drop");
Scene scene = new Scene(root, 400, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
//source events handlers
public void setOnDragDetected(Button source)
{
source.setOnDragDetected((MouseEvent event) -> {
/* drag was detected, start drag-and-drop gesture*/
System.out.println("onDragDetected");
/* allow any transfer mode */
Dragboard db = source.startDragAndDrop(TransferMode.ANY);
db.setDragView(source.snapshot(new SnapshotParameters(), null));
source.setVisible(false);
/* put a string on dragboard */
ClipboardContent content = new ClipboardContent();
content.putString(source.getText());
db.setContent(content);
event.consume();
});
}
public void setOnDragDone(Button source)
{
source.setOnDragDone((DragEvent event) -> {
/* the drag-and-drop gesture ended */
System.out.println("onDragDone");
/* if the data was successfully moved, clear it */
// if (event.getTransferMode() == TransferMode.MOVE) {
// source.setText("");
// }
event.consume();
});
}
//target event handlers
public void setOnDragOver(StackPane target)
{
target.setOnDragOver((DragEvent event) -> {
/* data is dragged over the target */
System.out.println("onDragOver");
/* accept it only if it is not dragged from the same node
* and if it has a string data */
if (event.getGestureSource() != target
&& event.getDragboard().hasString()) {
/* allow for both copying and moving, whatever user chooses */
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
event.consume();
});
}
public void setOnDragEntered(StackPane target)
{
target.setOnDragEntered((DragEvent event) -> {
/* the drag-and-drop gesture entered the target */
System.out.println("onDragEntered");
/* show to the user that it is an actual gesture target */
if (event.getGestureSource() != target
&& event.getDragboard().hasString()) {
target.setStyle("-fx-background-color: green;");
}
event.consume();
});
}
public void setOnDragExited(StackPane target)
{
target.setOnDragExited((DragEvent event) -> {
/* mouse moved away, remove the graphical cues */
target.setStyle("-fx-background-color: yellow;");
event.consume();
});
}
public void setOnDragDropped(StackPane target)
{
target.setOnDragDropped((DragEvent event) -> {
/* data dropped */
System.out.println("onDragDropped");
/* if there is a string data on dragboard, read it and use it */
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasString()) {
//target.setText(db.getString());
Button tempBoat = new Button(db.getString());
tempBoat.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
target.getChildren().clear();
target.getChildren().add(tempBoat);
success = true;
}
/* let the source know whether the string was successfully
* transferred and used */
event.setDropCompleted(success);
event.consume();
});
}
}
Related
I'm creating a basic animation with javaFX, I have 5 rectangles in a row, I have two cirlces, red and blue. The red cricle is set on the first rectangle and the blue one is set on the fifth rectangle.
The idea is: If I click the first rectangle I want the red circle to travel to the fifth rectangle (by a translation) and once it gets there the blue rectangle (which is on the fifth rectangle) travels to the first one, in another word they exchange positions. I used the AnimationTimer class in my logic but the porblem is that the animation of both cirlces is synchronized when the pressedMouse event happens and that's not what I want, what I want is that the animation of the blue circle starts once the red circle's one is finished. I want to understand why is that happening? Is it kinda multiple thread? In case it is, when I run the program the red circle gets stuck in the middle however the blue one travels out of the bounds and hides, however if I comment one of the cirlce's position code for update (the update method) the app runs correctly, I hope I get an answer and I'm so thankful.
Another question is: How to make my animation looks smoother because it stops for a fraction of a second and moves again.
here is my code:
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class animation extends Application {
AnchorPane root = new AnchorPane();
//create a matrix from rectangle class to stock 5 rectangle objects
rectangle rect [] = new rectangle [5];
//isMoving gets the true value when the animation starts
private boolean isMoving = false;
private int traveledDistance = 30;
#Override
public void start(Stage primaryStage) {
//add 5 rectangles on the anchorpane
//rect[0], rect[2] and rect[4] have BURLYWOOD color
//rect[1], rect[3] have DARKBLUE color
for(int i = 0; i<5; i++)
{
if(i%2 == 0)
{
rect[i] = new rectangle();
rect[i].setFill(Color.BURLYWOOD);
}
else
{
rect[i] = new rectangle();
rect[i].setFill(Color.DARKBLUE);
}
//set all 5 rectangles as empty
rect[i].setRectEmpty(true);
//set all the 5 rectangles one after the other along the x axis
rect[i].setTranslateX(i*60);
//add the 5 rectangles to the parent
root.getChildren().add(rect[i]);
}
//instantiation of two circles (c and d) from cirlce class
circle c = new circle(Color.RED);
c.setName("redCircle");
circle d = new circle(Color.BLUE);
d.setName("blueCircle");
//set the position of the red circle centered relatively to rect[0]
//rect[0] is no longer empty as it contains the red circle
c.setTranslateX(30);
c.setTranslateY(30);
rect[0].setCircle(c);
rect[0].setCircleName(c.getName());
rect[0].setRectEmpty(false);
root.getChildren().add(c);
//set the position of the blue circle centered relatively to rect[4]
d.setTranslateX(4*60 +30);
d.setTranslateY(30);
rect[4].setCircle(d);
rect[4].setCircleName(d.getName());
root.getChildren().add(d);
displayedScene(primaryStage);
//when the parent is clicked
root.setOnMousePressed(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event) {
//get the index of the clicked rectangle
int index = (int) event.getX()/60;
//if the clicked rectangle contains the red circle inside
if(!rect[index].isRectEmpty() && rect[index].getCircleName().equals("redCircle"))
{
Circle circle = rect[index].getCircle();
//update the postion of the red circle so that it occupies the last rectangle (rect[4])
update(index,5, circle);
//update the position of the blue circle so that it occupies the first rectangle(rect[0])
update(5,0, rect[4].getCircle());
}
}
});
}
//update method uses the AnimationTimer class
public void update(int initialPos, int lastPos, Circle circle)
{
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
updateCirclePosition(initialPos, lastPos, circle);
if(!isMoving)
{
this.stop();
}
}
};
timer.start();
}
public void updateCirclePosition(int initialPos, int lastPos, Circle circle)
{
int dx = 2;
if(initialPos>lastPos)
{
dx = -1*dx;
}
isMoving = true;
int distance = Math.abs((lastPos - initialPos)*60);
if(traveledDistance<distance-30)
{
circle.setTranslateX(circle.getTranslateX() + dx);
traveledDistance +=Math.abs(dx);
}
else{
isMoving = false;
traveledDistance = 30;
}
}
//load the Stage
public void displayedScene(Stage primaryStage)
{
Scene scene = new Scene(root, 300, 60);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
// circle class extends Circle
public class circle extends Circle
{
private String name;
public circle(Paint color) {
super(30, color);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
And here is rectangle class:
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
//rectangle class extends Rectangle
public class rectangle extends Rectangle {
private Circle circle;
private String circleName;
private boolean rectEmpty;
public rectangle() {
super(60, 60);
}
public Circle getCircle() {
return circle;
}
public void setCircle(Circle circle) {
this.circle = circle;
}
public boolean isRectEmpty() {
return rectEmpty;
}
public void setRectEmpty(boolean rectEmpty) {
this.rectEmpty = rectEmpty;
}
public String getCircleName() {
return circleName;
}
public void setCircleName(String circleName) {
this.circleName = circleName;
}
}
The following is an mre demonstrating the requested functionality.
Circles animation is done by animateCircles(). It uses TranslateTransition to translate a circle from one position to the other.
setOnFinished is used to start the next animation.
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Animation extends Application {
private static final double SQUARE_SIZE = 60, RADIUS = SQUARE_SIZE /2, ANIMATION_TIME = 1;
private final Pane root = new Pane();
private final Rectangle rect [] = new Rectangle [5];
private final Circle circles [] = new Circle[2];
private boolean isMoving = false, isSwapped = false;
#Override
public void start(Stage primaryStage) {
for(int i = 0; i<rect.length; i++) {
rect[i] = new Rectangle(SQUARE_SIZE, SQUARE_SIZE, i%2 == 0 ? Color.BURLYWOOD : Color.DARKBLUE);
//set all the 5 rectangles one after the other along the x axis
rect[i].setTranslateX(i*SQUARE_SIZE);
root.getChildren().add(rect[i]);
}
circles[0] = new Circle(RADIUS,Color.RED);
circles[1] = new Circle(RADIUS,Color.BLUE);
//set the position of the red circle centered to rect[0]
Point2D center = centerOf(rect[0]);
circles[0].setTranslateX(center.getX());
circles[0].setTranslateY(center.getY());
//set the position of the blue circle centered to rect[4]
center = centerOf(rect[4]);
circles[1].setTranslateX(center.getX());
circles[1].setTranslateY(center.getY());
root.getChildren().add(circles[0]);
root.getChildren().add( circles[1]);
Scene scene = new Scene(root, SQUARE_SIZE*rect.length, SQUARE_SIZE);
primaryStage.setScene(scene);
primaryStage.show();
root.setOnMousePressed(event -> animateCircles());
}
//return the center point
private Point2D centerOf(Rectangle rect) {
Bounds bounds = rect.getBoundsInParent();
double x = bounds.getMinX() + 0.5 * bounds.getWidth();
double y = bounds.getMinY() + 0.5 * bounds.getHeight();
return new Point2D(x, y);
}
private void animateCircles() {
if(isMoving) return;
TranslateTransition translateCircle0 = new TranslateTransition(Duration.seconds(ANIMATION_TIME), circles[0]);
translateCircle0.setToX( isSwapped ? centerOf(rect[0]).getX() : centerOf(rect[4]).getX());
TranslateTransition translateCircle1 = new TranslateTransition(Duration.seconds(ANIMATION_TIME), circles[1]);
translateCircle1.setToX( isSwapped ? centerOf(rect[4]).getX() : centerOf(rect[0]).getX());
translateCircle0.setOnFinished(e-> {
translateCircle1.play();
});
translateCircle1.setOnFinished(e-> {
isMoving = false;
isSwapped = ! isSwapped;
});
isMoving = true;
translateCircle0.play();
}
public static void main(String[] args) {
launch(args);
}
}
Alternatively you could implement animateCircles() using SequentialTransition:
private void animateCircles() {
if(isMoving) return;
TranslateTransition translateCircle0 = new TranslateTransition(Duration.seconds(ANIMATION_TIME), circles[0]);
translateCircle0.setToX( isSwapped ? centerOf(rect[0]).getX() : centerOf(rect[4]).getX());
TranslateTransition translateCircle1 = new TranslateTransition(Duration.seconds(ANIMATION_TIME), circles[1]);
translateCircle1.setToX( isSwapped ? centerOf(rect[4]).getX() : centerOf(rect[0]).getX());
SequentialTransition sequentialTransition = new SequentialTransition(translateCircle0, translateCircle1);
isMoving = true;
sequentialTransition.play();
sequentialTransition.setOnFinished(e-> {
isMoving = false;
isSwapped = ! isSwapped;
});
}
I created an application which generates a board with a grid pattern, consisting of nodes which hold square objects in javaFX, using GridPanel. Below is the current output:
I want to know how to return the coordinate of a node, after CLICKING on the node. I am aware I have to use an action listener of sorts, but I'm not entirely familiar when it comes to having node coordinates.
Below is the current source code, thank you very much.
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class MainApp extends Application {
private final double windowWidth = 1000;
private final double windowHeight = 1000;
/*n is amount of cells per row
m is amount of cells per column*/
private final int n = 50;
private final int m = 50;
double gridWidth = windowWidth / n;
double gridHeight = windowHeight / m;
MyNode[][] playfield = new MyNode[n][m];
public static void main(String[] args) {
launch(args);
}
#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( 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, windowWidth, windowHeight);
primaryStage.setScene( scene);
primaryStage.show();
primaryStage.setResizable(false);
primaryStage.sizeToScene();
}
public static class MyNode extends StackPane {
public MyNode(double x, double y, double width, double height) {
// create rectangle
Rectangle rectangle = new Rectangle( width, height);
rectangle.setStroke(Color.BLACK);
rectangle.setFill(Color.LIGHTGREEN);
// set position
setTranslateX(x);
setTranslateY(y);
getChildren().addAll(rectangle);
}
}
}
You can add mouse event handler to root :
root.setOnMousePressed(e->mousePressedOnRoot(e));
Where mousePressedOnRoot(e) is defined as
private void mousePressedOnRoot(MouseEvent e) {
System.out.println("mouse pressed on (x-y): "+e.getSceneX()+"-"+e.getSceneY());
}
Edit: alternatively you can add mouse event handler to each MyNode instance by adding setOnMousePressed(e->mousePressedOnNode(e)); to its constructor.
and add the method:
private void mousePressedOnNode(MouseEvent e) {
System.out.println("mouse pressed on (x-y): "+e.getSceneX()+"-"+e.getSceneY());
}
If you need the coordinates within the clicked node use e.getX() and e.getY()
I'm trying to build a space invaders like game.
I've drawn a square and I want to move it down incrementally by using a loop and thread.sleep. However, the square just gets drawn immediately. I understand there are animation paths that could be used but I want to stay low level and just use a coordinate system.
Is there a way of making a timeline animation by using a loop like this?
package com.company;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.shape.Rectangle;
public class Main extends Application {
public static void main(String[] args) {
// write your code here
launch(args);
}
public void start(Stage myStage) throws InterruptedException {
myStage.setTitle("space invaders");
Pane rootNode= new Pane();
Scene myScene=new Scene(rootNode, 400, 800);
myStage.setScene(myScene);
Rectangle r = new Rectangle();
myStage.show();
rootNode.getChildren().add(r);
r.setX(50);
r.setY(50);
r.setWidth(20);
r.setHeight(20);
for (int i = 0; i < 500; i++){
Thread.sleep(2000);
r.setTranslateY(i);
}
}
}
This is a terrible implementation. I would probably use AnimationTimer. This is done with Timeline. It's basically moving right or left. If you hit the right or left bound, drop then move in the opposite direction.
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javafx.animation.KeyFrame;
import javafx.animation.PauseTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
*
* #author blj0011
*/
public class JavaFXApplication343 extends Application
{
int invaderWidth = 30;
int invaderHeight = 10;
int gapBetweenInvaderX = 5;
int gapBetweenInvaderY = 5;
int locationTrackerX;
int locationTrackerY;
int screenWidth = 300;
int screenHeight = 400;
double timeBetweenFrames = .25;
boolean direction = true;
Timeline timeline;
#Override
public void start(Stage primaryStage)
{
Pane pane = new Pane();
locationTrackerX = (screenWidth - (invaderWidth * 6 + gapBetweenInvaderX * 5)) / 2;
locationTrackerY = (screenHeight - (invaderHeight * 6 + gapBetweenInvaderY * 5)) / 7;
List<Rectangle> invaders = new ArrayList();
for (int i = 0; i < 36; i++) {
Rectangle rectangle = new Rectangle(locationTrackerX, locationTrackerY, invaderWidth, invaderHeight);
rectangle.setFill(Color.YELLOW);
invaders.add(rectangle);
System.out.println(locationTrackerX);
locationTrackerX += invaderWidth + gapBetweenInvaderX;
if ((i + 1) % 6 == 0) {
locationTrackerX = (screenWidth / 2) - ((invaderWidth * 6 + gapBetweenInvaderX * 5) / 2);
locationTrackerY += invaderHeight + gapBetweenInvaderY;
}
}
timeline = new Timeline(new KeyFrame(Duration.seconds(timeBetweenFrames), (event) -> {
//Check to see if invader hits bounds
Optional<Rectangle> hitRightOptional = invaders.stream().filter(invader -> invader.getBoundsInLocal().getMaxX() >= pane.getWidth()).findFirst();
Optional<Rectangle> hitLeftOptional = invaders.stream().filter(invader -> invader.getBoundsInLocal().getMinX() <= 0).findFirst();
//Move invaders
if (hitRightOptional.isPresent()) {
invaders.forEach((tempInvader) -> tempInvader.setY(tempInvader.getY() + 10));
timeline.stop();
PauseTransition pause = new PauseTransition(Duration.seconds(timeBetweenFrames));
pause.setOnFinished((pauseEvent) -> {
invaders.forEach(invader -> invader.setX(invader.getX() - 10));
timeline.play();
});
pause.play();
direction = false;
}
else if (hitLeftOptional.isPresent()) {
invaders.forEach((tempInvader) -> tempInvader.setY(tempInvader.getY() + 10));
timeline.stop();
PauseTransition pause = new PauseTransition(Duration.seconds(timeBetweenFrames));
pause.setOnFinished((pauseEvent) -> {
invaders.forEach(invader -> invader.setX(invader.getX() + 10));
timeline.play();
});
pause.play();
direction = true;
}
else {
if (direction) {
invaders.forEach(invader -> invader.setX(invader.getX() + 10));
}
else {
invaders.forEach(invader -> invader.setX(invader.getX() - 10));
}
}
}));
timeline.setCycleCount(Timeline.INDEFINITE);
Button btn = new Button();
btn.setText("Start Game");
btn.setOnAction((ActionEvent event) -> {
timeline.play();
btn.setDisable(true);
});
pane.getChildren().addAll(invaders);
pane.setPrefSize(screenWidth, screenHeight);
VBox root = new VBox(pane, new StackPane(btn));
Scene scene = new Scene(root, screenWidth, screenHeight);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
Solved :
Task task = new Task<Void>() {
#Override public Void call() throws InterruptedException {
for (int i = 1; i <= 800; i=i+10) {
Thread.sleep(25);
//updateProgress(i, max);
r.setTranslateY(i);
}
return null;
}
};
new Thread(task).start();
I am creating am application that displays circles (of different colors) randomly within each cell of a gridPane.
What i want to do is create a "shuffle" button that changes the position of each circle randomly within the gridPane. However, I keep running into a slurry of problems.
Here is what i have so far. My two classes (have not added XML file):
Controller Class
public class viewController {
//My two Variables, a gridPane and a button
#FXML
private GridPane matrix;
#FXML
private Button shuffleBut;
//my eventHandler event that should (1) add circles to all the cells, and
(2) shuffle them amongst the cells in the gridPane.
void shuffle(ActionEvent e) {
Random r = new Random ();
int rowShuffle = r.next((4-0)+1);
int colShuffle = r.next((4-0)+1);
Circle newCircle = new Circle ();
matrix.add(newCircle, rowShuffle, colShuffle );
}
Main Class
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
// just load fxml file and display it in the stage:
Parent root = FXMLLoader.Load(getClass().getResource("mainUI.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
// main method to support non-JavaFX-aware environments:
public static void main(String[] args) {
// starts the FX toolkit, instantiates this class,
// and calls start(...) on the FX Application thread:
launch(args);
}
Here is an example that demos how to shuffle Circles around in a GridPane. If you add the Circles to an ArrayList, you can remove the Circles from the GridPane. Then you can shuffle the List. Finally, you can add the shuffled list back to the GridPane.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Control;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication314 extends Application
{
Random random = new Random();
int numberOfRows = 25;
int numberOfColumns = 25;
#Override
public void start(Stage primaryStage)
{
List<Circle> circles = new ArrayList();
for (int i = 0; i < numberOfColumns * numberOfRows; i++) {
circles.add(new Circle(10, getRandomColor()));
}
GridPane gridPane = new GridPane();
addCirclesToGridPane(gridPane, circles);
gridPane.setPadding(new Insets(20, 20, 20, 20));
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction((ActionEvent event) -> {
Collections.shuffle(circles);//Shuffle the List of Circles.
for(int i = 0; i < numberOfColumns * numberOfRows; i++)
{
Circle c = circles.get(i);
GridPane.setColumnIndex(c, i % numberOfColumns);
GridPane.setRowIndex(c, i / numberOfColumns);
}
});
VBox vBox = new VBox(gridPane, new StackPane(btn));
vBox.setMaxSize(Control.USE_COMPUTED_SIZE, Control.USE_COMPUTED_SIZE);
StackPane root = new StackPane(vBox);
root.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
Scene scene = new Scene(root);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
public void addCirclesToGridPane(GridPane gridPane, List<Circle> circles)
{
for (int i = 0; i < numberOfColumns * numberOfRows; i++) {
gridPane.add(circles.get(i), i % numberOfColumns, i / numberOfColumns);
}
}
public Color getRandomColor()
{
int r = random.nextInt(255);
int g = random.nextInt(255);
int b = random.nextInt(255);
return Color.rgb(r, g, b);
}
}
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);
}
}
}