With the following code (thanks to several posts here), I draw a rectangle, that I want to be resizable and movable. Two anchors (the upper left and lower right) do what I want, and the last one (lower middle) moves the rectangle, but the two first anchors do not follow the rectangle.
When I make them move, the Listener of them, resizes the rectangle.
package application;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
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.Rectangle;
import javafx.scene.shape.Shape;
import javafx.scene.shape.StrokeType;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
private Rectangle rectangle;
private Group group;
private Scene scene;
private Stage primaryStage;
private ObservableList<Double> Coins;
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage primaryStage) {
group = new Group();
rectangle = new Rectangle(200,200,400,300);
Coins = FXCollections.observableArrayList();
//UpperLeft
Coins.add(rectangle.getX());
Coins.add(rectangle.getY());
//LowerRight
Coins.add(rectangle.getX() + rectangle.getWidth());
Coins.add(rectangle.getY()+ rectangle.getHeight());
//Moving
Coins.add(rectangle.getX() + (rectangle.getWidth()/2));
Coins.add(rectangle.getY()+ (rectangle.getHeight()));
group.getChildren().addAll(createControlAnchorsFor(Coins));
group.getChildren().add(rectangle);
scene = new Scene(group,800,800);
primaryStage.setScene(scene);
primaryStage.show();
}
//@return a list of anchors which can be dragged around to modify points in the format [x1, y1, x2, y2...]
private ObservableList<Anchor> createControlAnchorsFor(final ObservableList<Double> points) {
ObservableList<Anchor> anchors = FXCollections.observableArrayList();
//Coin GaucheHaut
DoubleProperty xProperty = new SimpleDoubleProperty(points.get(0));
DoubleProperty yProperty = new SimpleDoubleProperty(points.get(1));
xProperty.addListener(new ChangeListener<Number>() {
@Override public void changed(ObservableValue<? extends Number> ov, Number oldX, Number x) {
System.out.println(oldX + " et " + x);
rectangle.setX((double) x);
rectangle.setWidth((double) rectangle.getWidth() -((double) x- (double) oldX));
anchors.get(2).setCenterX((double) x + rectangle.getWidth()/2 );
}
});
yProperty.addListener(new ChangeListener<Number>() {
@Override public void changed(ObservableValue<? extends Number> ov, Number oldY, Number y) {
rectangle.setY((double) y);
rectangle.setHeight((double) rectangle.getHeight() -((double) y- (double) oldY));
}
});
anchors.add(new Anchor(Color.GOLD, xProperty, yProperty));
//Coin DroiteBas
DoubleProperty xProperty2 = new SimpleDoubleProperty(points.get(2));
DoubleProperty yProperty2 = new SimpleDoubleProperty(points.get(3));
xProperty2.addListener(new ChangeListener<Number>() {
@Override public void changed(ObservableValue<? extends Number> ov, Number oldX, Number x) {
rectangle.setWidth((double) rectangle.getWidth() -((double) oldX- (double) x));
anchors.get(2).setCenterX((double) x - rectangle.getWidth()/2 );
}
});
yProperty2.addListener(new ChangeListener<Number>() {
@Override public void changed(ObservableValue<? extends Number> ov, Number oldY, Number y) {
rectangle.setHeight((double) rectangle.getHeight() -((double) oldY- (double) y));
anchors.get(2).setCenterY((double) y);
}
});
anchors.add(new Anchor(Color.GOLD, xProperty2, yProperty2));
//Moving
DoubleProperty xPropertyM = new SimpleDoubleProperty(points.get(4));
DoubleProperty yPropertyM = new SimpleDoubleProperty(points.get(5));
xPropertyM.addListener(new ChangeListener<Number>() {
@Override public void changed(ObservableValue<? extends Number> ov, Number oldX, Number x) {
rectangle.setX((double) x - rectangle.getWidth()/2 );
//anchors.get(0).setCenterX((double) x- rectangle.getWidth()/2);
//anchors.get(0).setVisible(false);
}
});
yPropertyM.addListener(new ChangeListener<Number>() {
@Override public void changed(ObservableValue<? extends Number> ov, Number oldY, Number y) {
rectangle.setY((double) y - rectangle.getHeight() );
Coins.set(1, (double) y);
}
});
anchors.add(new Anchor(Color.GOLD, xPropertyM, yPropertyM));
return anchors;
}
//a draggable anchor displayed around a point.
class Anchor extends Circle {
private final DoubleProperty x, y;
Anchor(Color color, DoubleProperty x, DoubleProperty y) {
super(x.get(), y.get(), 20);
setFill(color.deriveColor(1, 1, 1, 0.5));
setStroke(color);
setStrokeWidth(2);
setStrokeType(StrokeType.OUTSIDE);
this.x = x;
this.y = y;
x.bind(centerXProperty());
y.bind(centerYProperty());
enableDrag();
}
//make a node movable by dragging it around with the mouse.
private void enableDrag() {
final Delta dragDelta = new Delta();
setOnMousePressed(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent mouseEvent) {
// record a delta distance for the drag and drop operation.
dragDelta.x = getCenterX() - mouseEvent.getX();
dragDelta.y = getCenterY() - mouseEvent.getY();
getScene().setCursor(Cursor.MOVE);
}
});
setOnMouseReleased(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent mouseEvent) {
getScene().setCursor(Cursor.HAND);
}
});
setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent mouseEvent) {
double newX = mouseEvent.getX() + dragDelta.x;
if (newX > 0 && newX < getScene().getWidth()) {
setCenterX(newX);
}
double newY = mouseEvent.getY() + dragDelta.y;
if (newY > 0 && newY < getScene().getHeight()) {
setCenterY(newY);
}
//Recompute screen;
group.getChildren().add(rectangle);
scene = new Scene(group,800,800);;
primaryStage.setScene(scene);
}
});
setOnMouseEntered(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent mouseEvent) {
if (!mouseEvent.isPrimaryButtonDown()) {
getScene().setCursor(Cursor.HAND);
}
}
});
setOnMouseExited(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent mouseEvent) {
if (!mouseEvent.isPrimaryButtonDown()) {
getScene().setCursor(Cursor.DEFAULT);
}
}
});
}
//records relative x and y co-ordinates.
private class Delta { double x, y; }
}
}
Any idea, what and where I should add something ?
Since the "handles" are always in the same position relative to the rectangle, I would bind their position to the position of the rectangle. You can achieve this with
where the argument is some
ObservableValue<Number>
.Then in the dragging handlers, just move the
Rectangle
as required (the computations are slightly complex but not too bad). Since the positions of the circles are bound, they will follow the rectangle.Here's one possible implementation that uses this strategy: