Using JavaFX8 to Drag Connected Shapes


Objective

In this example we are going to create three connected circles as per the image below:

001-connectedCircles

and we will be able to drag these circles around the screen using the mouse:

002-connectedCircles
The Code

Create the class examples001.ConnectedShapes.java as follows:

ConnectedShapes

/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package examples001;

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.StrokeLineCap;
import javafx.stage.Stage;

/**
*
* @author johndunning
*/
public class ConnectedShapes extends Application
{

double orgSceneX, orgSceneY;

private EventHandler<MouseEvent> mousePressedEventHandler = (t) ->
{
orgSceneX = t.getSceneX();
orgSceneY = t.getSceneY();

// bring the clicked circle to the front

Circle c = (Circle) (t.getSource());
c.toFront();
};

private EventHandler<MouseEvent> mouseDraggedEventHandler = (t) ->
{
double offsetX = t.getSceneX() - orgSceneX;
double offsetY = t.getSceneY() - orgSceneY;

Circle c = (Circle) (t.getSource());

c.setCenterX(c.getCenterX() + offsetX);
c.setCenterY(c.getCenterY() + offsetY);

orgSceneX = t.getSceneX();
orgSceneY = t.getSceneY();
};

private Circle createCircle(double x, double y, double r, Color color)
{
Circle circle = new Circle(x, y, r, color);

circle.setCursor(Cursor.CROSSHAIR);

circle.setOnMousePressed(mousePressedEventHandler);
circle.setOnMouseDragged(mouseDraggedEventHandler);

return circle;
}

private Line connect(Circle c1, Circle c2)
{
Line line = new Line();

line.startXProperty().bind(c1.centerXProperty());
line.startYProperty().bind(c1.centerYProperty());

line.endXProperty().bind(c2.centerXProperty());
line.endYProperty().bind(c2.centerYProperty());

line.setStrokeWidth(1);
line.setStrokeLineCap(StrokeLineCap.BUTT);
line.getStrokeDashArray().setAll(1.0, 4.0);

return line;
}

@Override
public void start(Stage primaryStage)
{
primaryStage.setTitle("Connected Shapes");

//
Group root = new Group();
Scene scene = new Scene(root, 500, 260);

// circles
Circle redCircle = createCircle(100, 50, 30, Color.RED);
Circle blueCircle = createCircle(200, 150, 20, Color.BLUE);
Circle greenCircle = createCircle(400, 100, 40, Color.GREEN);

Line line1 = connect(redCircle, blueCircle);
Line line2 = connect(redCircle, greenCircle);
Line line3 = connect(greenCircle, blueCircle);

// add the circles
root.getChildren().add(redCircle);
root.getChildren().add(blueCircle);
root.getChildren().add(greenCircle);

// add the lines
root.getChildren().add(line1);
root.getChildren().add(line2);
root.getChildren().add(line3);

// bring the circles to the front of the lines
redCircle.toFront();
blueCircle.toFront();
greenCircle.toFront();

// set the scene
primaryStage.setScene(scene);
primaryStage.show();

}

/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}

}

Run the code by right clicking on the file and selecting Run File.

Reviewing the Code

Setting the Scene

First we create a Stage, set the Scene and initialise the Scene with a Group to which we will add our Nodes as children.

The Group is called root and we add our Nodes (in this case 3 Circles and 3 Lines) to it by calling root.getChildren().add() with our Node.

Once the scene is set on the Stage, we show the Stage

Creating the Circles

To create each circle we use the createCircle method which takes x,y, radius and color as arguments.

We then create the circle and set the cursor when we move the mouse over the circle to be a crosshair.

Finally we add two mouseEventHandlers to the circle:

mousePressedEventHandler to bring our circle to the front and to determine the position of the circle on the page when we click on it, and

mouseDraggedEventHandler to move the circle according to how far we drag our mouse.

EventHandlers

Notice how our event handlers are declared using lambda expressions.

Lambda Expressions

Instead of writing

private EventHandler<MouseEvent> mouse1PressedEventHandler = new EventHandler<MouseEvent>()
{
@Override
public void handle(MouseEvent t)
{
orgSceneX = t.getSceneX();
orgSceneY = t.getSceneY();

// bring the clicked circle to the front
Circle c = (Circle) (t.getSource());
c.toFront();
}
};

we can write

private EventHandler<MouseEvent> mousePressedEventHandler = (t) ->
{
orgSceneX = t.getSceneX();
orgSceneY = t.getSceneY();

// bring the clicked circle to the front
Circle c = (Circle) (t.getSource());
c.toFront();
};

and similarly for mouseDraggedEventHandler.

Creating the Lines

We use the connect method to connect two circles with a dashed line.

We first create a line and set the stroke width, line cap and dash array.

Then we bind the x and y properties of one end of the line to the centre of one of the circles, and bind the x and y properties of the other end of the line to the other circle.

Source Code

The source code for this example can be found on GitHub at https://github.com/johndunning/JavaFX8Examples

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s