I am working on a project that has to do with animation in javaFx, I wrote the GUI for it and it is a Stick Figure. I am trying to make the stick figure walk to the right side of the pane and then when it touches the right side turn and go back the other way all the way to the left wall of the pane. I have this code that is a moving ball that does exactly what I want for my stick figure to do but I cannot seem to modify that code into my stick figure program. any ideas on how to do this? both codes are below:
package animationdemo;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class MovingBallDemo_3 extends Application {
#Override
public void start(Stage primaryStage) {
BallPane ballPane = new BallPane(); // Create a ball pane
// Pause and resume animation
ballPane.setOnMousePressed(e -> ballPane.pause());
ballPane.setOnMouseReleased(e -> ballPane.play());
// Create a scene and place it in the stage
Scene scene = new Scene(ballPane, 250, 150);
primaryStage.setTitle("Bouncing Ball Control"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
}
public static void main(String[] args) {
launch(args);
}
}
class BallPane extends Pane {
public final double radius = 20;
private double x = 2 * radius, y = 3 * radius;
private double dx = 3; // Number of pixels to move each time
private Circle circle = new Circle(x, y, radius);
private Timeline animation;
public BallPane() {
circle.setFill(Color.RED); // Set ball color
getChildren().add(circle); // Place a ball into this pane
// Create the animation for 25 millisecond events
animation = new Timeline(new KeyFrame(Duration.millis(25), e -> moveBall()));
animation.setCycleCount(Timeline.INDEFINITE);
animation.play(); // Start animation
}
public void play() {
animation.play();
}
public void pause() {
animation.pause();
}
// Move the ball. When a wall is encountered, reverse direction
protected void moveBall() {
if (x <= radius || x >= getWidth() - radius) {
dx *= -1; // Change direction
}
// Adjust ball position
x += dx;
circle.setCenterX(x);
}
}
the above is the animation that I want the stick figure GUI to do. here is my attempt of merging my code in with the code above to make the stick figure work but nothing happens:
package Stickfigure;
import java.awt.Graphics;
import javafx.animation.KeyFrame;
import javafx.animation.PathTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Stickfigure extends Application {
#Override
public void start(Stage primaryStage) {
BallPane ballPane = new BallPane();
ballPane.setOnMousePressed(e -> ballPane.pause());
ballPane.setOnMouseReleased(e -> ballPane.play());
Circle circle = new Circle(100, 100, 0);//head
Circle circle1 = new Circle(120, 80, 50);//eye
circle1.setRadius(5);//radius of eye
circle.setRadius(50);//radius of head
circle.setStroke(Color.BLACK);//circle color
circle1.setStroke(Color.BLACK);//circle color
circle.setFill(null);//makes the head empty(no brain haha)
circle.setStrokeWidth(5);//sets the line thickness of circle (head)
Arc arc = new Arc();//mouth
arc.setCenterX(110.0f);//mouth position
arc.setCenterY(120.0f);//mouth position
arc.setRadiusX(35.0f);//mouth size
arc.setRadiusY(25.0f);//mouth size
arc.setStartAngle(1.0f);//angle of mouth
arc.setLength(5.0f);//length of mouth
arc.setType(ArcType.ROUND);
Line line1 = new Line(100, 250, 100, 150); //body of stick figure
Line line2 = new Line(); //left leg
Line line3 = new Line();//right leg
Line line4 = new Line();//right arm
Line line5 = new Line();//left arm
line2.setStartX(30.0f); //left leg starting position y
line2.setStartY(350.0f);//left leg starting position y
line2.setEndX(100.0f);//left leg end pos x
line2.setEndY(250.0f);//left leg end pos y
line3.setStartX(200.0f); //right leg start pos x
line3.setStartY(350.0f);// right leg start pos y
line3.setEndX(100.0f); //right leg end pos x
line3.setEndY(250.0f); //right leg end pos y
line4.setStartX(100.0f);//right arm start pos x
line4.setStartY(200.0f); //right arm start pos y
line4.setEndX(200.0f); //right arm end pos x
line4.setEndY(170.0f); //right arm end pos y
line5.setStartX(30.0f);//left arm arm statt pos x
line5.setStartY(250.0f); // left arm start pos y
line5.setEndX(100.0f);//left arm end pos x
line5.setEndY(200.0f);//left arm end pos y
line1.setStrokeWidth(5); //thickness of line
line1.setStroke(Color.BLACK);//color of line
line2.setStrokeWidth(5);//thickness of line
line2.setStroke(Color.BLACK);//color of line
line3.setStrokeWidth(5);//thickness of line
line3.setStroke(Color.BLACK);//color of line
line4.setStrokeWidth(5);//thickness of line
line4.setStroke(Color.BLACK);//color of line
line5.setStrokeWidth(5);//thickness of line
line5.setStroke(Color.BLACK);//color of line
// Create a pane to hold the circle
ballPane.getChildren().add(circle); //adds circle to picture
ballPane.getChildren().add(circle1);//adds circle to picture
ballPane.getChildren().add(line1);//adds line
ballPane.getChildren().add(line2);//adds line
ballPane.getChildren().add(line3);//adds line
ballPane.getChildren().add(line4);//adds line
ballPane.getChildren().add(line5);//adds line
ballPane.getChildren().add(arc);//adds arc
Scene scene = new Scene( ballPane, 400, 400);
primaryStage.setTitle("stick figure");//title
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
class BallPane extends Pane {
public final double radius = 20;
private double x = 2 * radius, y = 3 * radius;
private double dx = 3; // Number of pixels to move each time
private Timeline animation;
public BallPane() {
// Create the animation for 25 millisecond events
animation = new Timeline(new KeyFrame(Duration.millis(25), e -> moveBall()));
animation.setCycleCount(Timeline.INDEFINITE);
animation.play(); // Start animation
}
public void play() {
animation.play();
}
public void pause() {
animation.pause();
}
// Move the ball. When a wall is encountered, reverse direction
protected void moveBall() {
if (x <= radius || x >= getWidth() - radius) {
dx *= -1; // Change direction
}
// Adjust ball position
x += dx;
}
}
}
You are not doing any moving in the moveBall() method. You just change local values. Have a look at the Node class from which Pane is inheriting and the translateXProperty. You want to change that, not just your fields.
You even had the actual translation in your Ball class: circle.setCenterX(x);
protected void moveBall() {
if (x <= radius || x >= getWidth() - radius) {
dx *= -1; // Change direction
}
// Adjust ball position
x += dx;
//#######################################
setTranslateX(x); // Move the Pane!!! ###
//#######################################
}
Related
I am new to JavaFX and I am creating a simple program. What I'm trying to achieve is to create a ball every 5 seconds that bounces off the walls and all balls should move every tens times per second (10 milliseconds). Also, feel free to leave other suggestions about my code.
Here's the source code:
public class Main extends Application {
#Override
public void start(Stage stage) {
//Sets the title, adds a group, and background color
BorderPane canvas = new BorderPane();
Scene scene = new Scene(canvas, 640, 480, Color.WHITE);
Circle ball = new Circle(10, Color.RED);
ball.relocate(0, 10);
canvas.getChildren().add(ball);
stage.setTitle("Title");
stage.setScene(scene);
stage.show();
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(20), new EventHandler<ActionEvent>() {
double dx = 5; //Step on x or velocity
double dy = 3; //Step on y
#Override
public void handle(ActionEvent t) {
//move the ball
ball.setLayoutX(ball.getLayoutX() + dx);
ball.setLayoutY(ball.getLayoutY() + dy);
Bounds bounds = canvas.getBoundsInLocal();
//If the ball reaches the left or right border make the step negative
if(ball.getLayoutX() <= (bounds.getMinX() + ball.getRadius()) ||
ball.getLayoutX() >= (bounds.getMaxX() - ball.getRadius()) ){
dx = -dx;
}
//If the ball reaches the bottom or top border make the step negative
if((ball.getLayoutY() >= (bounds.getMaxY() - ball.getRadius())) ||
(ball.getLayoutY() <= (bounds.getMinY() + ball.getRadius()))){
dy = -dy;
}
}
}));
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
public static void main(String[] args) {
launch(args);
}
}
Here is one way to do it as suggested in comments:
Create a custom object that holds the ball and its orientation information.
Add the newly created object in the list and the ball in canvas.
Loop through all balls and position them based on their orientation information.
When your desired time is reached add a new ball.
To keep it simple, you don't need any concurrent related stuff. Maybe using them will improve the implementation (but I am not touching that now). :-)
Here is a demo of using the points I mentioned.
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
public class CanvasBallCreation_Demo extends Application {
List<Ball> balls = new ArrayList<>();
BorderPane canvas;
double dx = 5; //Step on x or velocity
double dy = 3; //Step on y
double refresh = 20;//ms
double addBallDuration = 5000;//ms
double temp = 0;
SecureRandom rnd = new SecureRandom();
#Override
public void start(Stage stage) {
canvas = new BorderPane();
Scene scene = new Scene(canvas, 640, 480, Color.WHITE);
addBall();
stage.setTitle("Title");
stage.setScene(scene);
stage.show();
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(refresh), e->moveBalls()));
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
private void addBall(){
Ball ball = new Ball();
balls.add(ball);
canvas.getChildren().add(ball.getBall());
}
private void moveBalls() {
temp = temp + refresh;
if(temp>addBallDuration){
temp = 0;
addBall();
}
Bounds bounds = canvas.getBoundsInLocal();
balls.forEach(obj -> {
Circle ball = obj.getBall();
double tx = obj.getTx();
double ty = obj.getTy();
ball.setLayoutX(ball.getLayoutX() + dx*tx);
ball.setLayoutY(ball.getLayoutY() + dy*ty);
//If the ball reaches the left or right border make the step negative
if (ball.getLayoutX() <= (bounds.getMinX() + ball.getRadius()) ||
ball.getLayoutX() >= (bounds.getMaxX() - ball.getRadius())) {
obj.setTx(-tx);
}
//If the ball reaches the bottom or top border make the step negative
if ((ball.getLayoutY() >= (bounds.getMaxY() - ball.getRadius())) ||
(ball.getLayoutY() <= (bounds.getMinY() + ball.getRadius()))) {
obj.setTy(-ty);
}
});
}
class Ball {
Circle ball = new Circle(10, Color.RED);
double tx = 1;
double ty = 1;
public Ball(){
// Placing the ball at a random location between 0,0 and 10,10 to generate random paths
ball.relocate(rnd.nextInt(10), rnd.nextInt(10));
}
public Circle getBall() {
return ball;
}
public double getTx() {
return tx;
}
public void setTx(double tx) {
this.tx = tx;
}
public double getTy() {
return ty;
}
public void setTy(double ty) {
this.ty = ty;
}
}
}
There are a few options, java.util.concurrent.TimeUnit or Thread.sleep.
You may want to look at both, and if you need two operations going at once (creating new balls, balls moving) I would look into using a new thread for each ball.
You can read more about using threads by using these articles, and your simple program actually seems like a great use case for learning about them.
https://www.geeksforgeeks.org/multithreading-in-java/
https://www.tutorialspoint.com/java/java_multithreading.htm
https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html
In my application, I'm drawing a lot of circles of different radii on a javafx canvas using the fillOval and strokeOval methods. I am trying to optimize this draw operation by drawing only those circles which will be visible(partially also) in my screen. So I want to know is there a method that can tell me that drawing this circle, text, line, image etc. will be visible in the screen.
package sample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Main extends Application {
Pane pane;
Canvas canvas;
double transX = 0.0, transY = 0.0;
public void draw() {
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.clearRect(canvas.getLayoutX(), canvas.getLayoutY(), canvas.getWidth(), canvas.getHeight());
gc.setStroke(Color.FIREBRICK);
gc.setFill(Color.TRANSPARENT);
gc.setLineWidth(2.0);
for(int i = 1; i <= 10000; ++i) {
double r = 10 * i;
// some function to check the below drawing operations will be visible or not
// Pls. note centre of the circles need not be at the centre of the screen, if canvas is panned to right/left/top/bottom by mouse drag operation where translation values will be stored in transX and transY
gc.fillOval(pane.getWidth() / 2 - r + transX, pane.getHeight() / 2 - r + transY, 2 * r, 2 * r);
gc.strokeOval(pane.getWidth() / 2 - r + transX, pane.getHeight() / 2 - r + transY, 2 * r, 2 * r);
}
}
#Override
public void start(Stage primaryStage) {
pane = new Pane();
canvas = new Canvas();
canvas.layoutXProperty().bind(pane.layoutXProperty());
canvas.layoutYProperty().bind(pane.layoutYProperty());
canvas.widthProperty().bind(pane.widthProperty());
canvas.heightProperty().bind(pane.heightProperty());
pane.widthProperty().addListener((observableValue, number, t1) -> draw());
pane.heightProperty().addListener((observableValue, number, t1) -> draw());
pane.getChildren().addAll(canvas);
primaryStage.setScene(new Scene(pane, primaryStage.getWidth(), primaryStage.getHeight()));
primaryStage.setTitle("Canvas Demo");
primaryStage.setMaximized(true);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
I have 2 rectangles representing the paddles for my Pong game. I use W/S for one rectangle and UP/DOWN for the second rectangle. When I press W to move one rectangle and then press UP to move the second rectangle, the first rectangle will stop moving and then the second rectangle will move. How do I make it so both rectangles can move simultaneously?
GraphicsContext gc;
Rectangle player11;
Rectangle player22;
Circle ball;
private int y1;
private int p1y = 381;
private int y2;
private int p2y = 381;
AnimateObjects animate;
Canvas canvas;
private AnimationTimer timer = new AnimationTimer() {
public void handle(long now) {
// update paddle positions
p1y += y1;
p2y += y2;
if (p1y < 0) {
p1y = 0;
}
if (p2y < 0) {
p2y = 0;
}
player11.setY(p1y);
player22.setY(p2y);
}
};
public EventHandler<KeyEvent> keyReleased = new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
// set movement to 0, if the released key was responsible for the paddle
switch (event.getCode()) {
case W:
case S:
y1 = 0;
break;
case UP:
case DOWN:
y2 = 0;
break;
}
}
};
private EventHandler<KeyEvent> keyPressed = new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
// start movement according to key pressed
switch (event.getCode()) {
case W:
y1 = -6;
break;
case S:
y1 = 6;
break;
case UP:
y2 = -6;
break;
case DOWN:
y2 = 6;
break;
}
}
};
public static void main(String[] args) {
launch();
}//main
public void start(Stage stage) {
stage.setTitle("Pong");
Group root = new Group();
canvas = new Canvas(1000, 800);
root.getChildren().add(canvas);
Scene scene = new Scene(root, Color.GREEN);
stage.setScene(scene);
player11 = new Rectangle(30, p1y, 20, 70);
player11.setFill(Color.RED);
player22 = new Rectangle(750, p2y, 20, 70);
player22.setFill(Color.BLUE);
root.getChildren().add(player11);
root.getChildren().add(player22);
scene.setOnKeyPressed(keyPressed);
scene.setOnKeyReleased(keyReleased);
ball = new Circle(10, Color.DARKSLATEBLUE);
root.getChildren().add(ball);
ball.relocate(500,350);
gc = canvas.getGraphicsContext2D();
animate = new AnimateObjects();
animate.start();
stage.show();
}//start
public class AnimateObjects extends AnimationTimer {
public void handle(long now) {
}//handle method in AnimateObjects class
}//AnimateObjects class
}//pong class
You need to capture KeyCodes. Use up, down, z, and x to control paddles. The right paddle will not move beyond the upper and lower bounds of the gameboard. The left paddle will. More comments in the code! Here is a working example.
import java.util.HashSet;
import java.util.Set;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class App extends Application
{
Rectangle leftPaddle, rightPaddle;
AnimationTimer gameLoop;
Set<KeyCode> input;
Pane gameBoard;
#Override
public void start(Stage primaryStage)
{
leftPaddle = new Rectangle(7, 100, Color.BLACK);
leftPaddle.setX(3);
leftPaddle.setY(0);
rightPaddle = new Rectangle(7, 100, Color.BLACK);
rightPaddle.setX(500 - 10);
rightPaddle.setY(0);
input = new HashSet(); //This set is used to keep up with keys that are currently being pressed.
gameBoard = new Pane(leftPaddle, rightPaddle);
VBox.setVgrow(gameBoard, Priority.ALWAYS);
gameBoard.setOnKeyPressed(event -> input.add(event.getCode()));//add keys that are currently being pressed to the set
gameBoard.setOnKeyReleased(event -> input.remove(event.getCode()));//remove keys from the set after they are released
gameLoop = new AnimationTimer()
{
#Override
public void handle(long l)
{
movePaddle();//Call method to move paddle based on certain keys in the set
//System.out.println("playing");
}
};
Button btnStartGame = new Button("Play");
btnStartGame.setMaxWidth(Double.MAX_VALUE);
btnStartGame.setOnAction((event) -> {
gameBoard.requestFocus();//Request gameboard focus to capture keyevents
gameLoop.start();
btnStartGame.setDisable(true);
});
VBox root = new VBox(gameBoard, btnStartGame);
Scene scene = new Scene(root, 500, 500);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
//Use to move paddles based on keycodes in set
private void movePaddle()
{
if (input.contains(KeyCode.UP)) {
rightPaddle.setY(rightPaddle.getY() - 10);
if (rightPaddle.getY() < 0) {
rightPaddle.setY(0);
}
}
else if (input.contains(KeyCode.DOWN)) {
rightPaddle.setY(rightPaddle.getY() + 10);
if (rightPaddle.getY() + rightPaddle.getHeight() > gameBoard.getHeight()) {
rightPaddle.setY(gameBoard.getHeight() - rightPaddle.getHeight());
}
}
if (input.contains(KeyCode.Z)) {
leftPaddle.setY(leftPaddle.getY() - 10);
}
else if (input.contains(KeyCode.X)) {
leftPaddle.setY(leftPaddle.getY() + 10);
}
}
}
I am making a javafx application that creates a bounded rectangle around the circles the user creates when primary mouse clicking. The user can also remove a circle with the secondary mouse button and the bounding rectangle should react accordingly and use the remaining circles as its upper and lower limits. The problem with my program is that it does not work when I try to remove more than one circle in a row (it only removes and resizes for the last circle)
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Collections;
public class BoundRectangle extends Application {
double minX, maxX, minY, maxY;
Pane pane = new Pane();
Rectangle rectangle;
ArrayList<Circle> allCircles = new ArrayList<>();
#Override
public void start(Stage primaryStage) {
VBox mainBox = new VBox();
Pane mainPane = new Pane(mainBox);
mainPane.setPadding(new Insets(10, 10, 10, 10));
pane.getChildren().addAll(mainPane);
mainBox.setLayoutX(10);
mainBox.setLayoutY(10);
pane.setOnMouseClicked(e -> {
if (e.getButton() == MouseButton.PRIMARY) {
Circle circle = new Circle(e.getX(), e.getY(), 10);
allCircles.add(circle);
pane.getChildren().add(drawRectangle());
pane.getChildren().add(circle);
System.out.println("maxX " + maxX);
System.out.println("maxY " + maxY);
System.out.println("minX " + minX);
System.out.println("minY " + minY);
circle.setOnMouseClicked(evt -> {
if (evt.getButton() == MouseButton.SECONDARY) {
pane.getChildren().remove(circle);
allCircles.remove(circle);
pane.getChildren().add(drawRectangle());
}
});
circle.setStroke(Color.BURLYWOOD);
circle.setStrokeWidth(3);
circle.setFill(Color.TRANSPARENT);
}
});
Scene scene = new Scene(pane, 600, 600);
primaryStage.setScene(scene);
primaryStage.setTitle("click circles, make rectangle");
primaryStage.show();
}
public Rectangle drawRectangle() {
refresh();
getMinMax();
if (pane.getChildren().size() == 1)
{
Rectangle rect0 = new Rectangle(0, 0, 0, 0);
return rect0;
}
Rectangle boundingRect = new Rectangle();
boundingRect.setX(minX - 10 - 2);
boundingRect.setY(minY - 10 - 2);
boundingRect.setWidth(maxX - minX + 2.0 * 10 + 2);
boundingRect.setHeight(maxY - minY + 2.0 * 10 + 2);
boundingRect.setStroke(Color.BLACK);
boundingRect.setStrokeWidth(2);
boundingRect.setFill(Color.TRANSPARENT);
return boundingRect;
}
public void getMinMax() {
maxY = allCircles.get(0).getCenterY();
minY = allCircles.get(0).getCenterY();
maxX = allCircles.get(0).getCenterX();
minX = allCircles.get(0).getCenterX();
for (Circle c : allCircles) {
if (c.getCenterX() < minX)
minX = c.getCenterX();
if (c.getCenterX() > maxX)
maxX = c.getCenterX();
if (c.getCenterY() < minY)
minY = c.getCenterY();
if (c.getCenterY() > maxY)
maxY = c.getCenterY();
}
}
private void refresh() {
ObservableList<Node> list = pane.getChildren();
for (Node c : list) {
if (c instanceof Rectangle) {
pane.getChildren().remove(c);
break;
}
}
}
public static void main(String[] args) {
Application.launch(args);
}
}
The last circle you add appears on top of the rectangle; however the rectangle appears on top of all the other circles. So only the last circle added will get the mouse clicks (clicks on other circles are targeted to the rectangle).
The quickest fix is to make the rectangle mouse transparent:
boundingRect.setMouseTransparent(true);
A better solution overall might be just to create one rectangle and update its x, y, width and height properties. That way you can just ensure the rectangle is added once and remains at the bottom of the stack.
I have a program where 2 circles are dragged in a pane. There is also a line connecting them and the distance displayed above it. My problem lies when I drag the circles at a slow pace with the mouse they move fine, but when I move it more quickly the circles stop.
here is where the circle drag is calculated
pane.setOnMouseDragged(e -> {
if (circle1.contains(e.getX(), e.getY())) {
pane.getChildren().clear();
circle1.setCenterX(e.getX());
circle1.setCenterY(e.getY());
pane.getChildren().addAll(getLine(circle1, circle2), circle1,
circle2, getText(circle1, circle2));
}
else if (circle2.contains(e.getX(), e.getY())) {
pane.getChildren().clear();
circle2.setCenterX(e.getX());
circle2.setCenterY(e.getY());
pane.getChildren().addAll(getLine(circle1, circle2), circle1,
circle2, getText(circle1, circle2));
}
});
I think what is happening is that when the mouse moves quickly, the distance moved between processing two consecutive events takes it outside the bounds of the circle, so the if condition becomes false. You probably need to register the mouse handlers on the circles themselves, instead of the pane. (As an aside, why clear and rebuild the pane, instead of just update the line?)
Here's an example using these techniques:
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
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.Line;
import javafx.stage.Stage;
public class DraggingCircles extends Application {
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
Circle circle1 = createDraggingCircle(50, 50, 25, Color.BLUE);
Circle circle2 = createDraggingCircle(350, 350, 25, Color.RED);
Line line = new Line();
line.startXProperty().bind(circle1.centerXProperty());
line.startYProperty().bind(circle1.centerYProperty());
line.endXProperty().bind(circle2.centerXProperty());
line.endYProperty().bind(circle2.centerYProperty());
pane.getChildren().addAll(circle1, circle2, line);
Scene scene = new Scene(pane, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private Circle createDraggingCircle(double x, double y, double radius, Color fill) {
Circle circle = new Circle(x, y, radius, fill);
ObjectProperty<Point2D> mouseLocation = new SimpleObjectProperty<>();
circle.setOnMousePressed(e -> {
mouseLocation.set(new Point2D(e.getX(), e.getY()));
});
circle.setOnMouseDragged(e -> {
double deltaX = e.getX() - mouseLocation.get().getX();
double deltaY = e.getY() - mouseLocation.get().getY();
circle.setCenterX(circle.getCenterX() + deltaX);
circle.setCenterY(circle.getCenterY() + deltaY);
mouseLocation.set(new Point2D(e.getX(), e.getY()));
});
return circle ;
}
public static void main(String[] args) {
launch(args);
}
}