This question already has an answer here:
How to change a shape property using its border in JavaFX?
(1 answer)
Closed 5 years ago.
I am try to allow users to drag/resize a rectangle in javafx.
Rectangle in javafx uses TopLeft-X, Y, height and width to draw.
What I am trying to do is to have 4 handles at each corner of the rectangle to allow users to drag the handle. Which will result in resizing the rectangle.
My application throws a stackoverflow exception which I know is caused by the recursive call of the listener.
Did i forget something? Or is there some listener that is unnecessary?
public class Handler extends Circle
{
private double deltaX;
private double deltaY;
private static final double DEFAULT_RADIUS = 10;
private static final Color DEFAULT_COLOR = Color.GOLD;
private DoubleProperty xProperty;
private DoubleProperty yProperty;
public Handler(DoubleProperty xProperty, DoubleProperty yProperty)
{
super(DEFAULT_RADIUS, DEFAULT_COLOR);
this.xProperty = xProperty;
this.yProperty = yProperty;
init();
}
private void init()
{
setFill(DEFAULT_COLOR.deriveColor(1, 1, 1, 0.5));
setStroke(DEFAULT_COLOR);
setStrokeWidth(2);
setStrokeType(StrokeType.OUTSIDE);
centerXProperty().bind(xProperty);
centerYProperty().bind(yProperty);
setOnMousePressed(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
mouseEvent.consume();
deltaX = getCenterX() - mouseEvent.getX();
deltaY = getCenterY() - mouseEvent.getY();
getScene().setCursor(javafx.scene.Cursor.MOVE);
}
});
setOnMouseReleased(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
getScene().setCursor(javafx.scene.Cursor.HAND);
}
});
setOnMouseEntered(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
if (!mouseEvent.isPrimaryButtonDown())
{
getScene().setCursor(javafx.scene.Cursor.HAND);
}
}
});
setOnMouseExited(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
if (!mouseEvent.isPrimaryButtonDown())
{
getScene().setCursor(javafx.scene.Cursor.DEFAULT);
}
}
});
setOnMouseDragged(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
double newX = mouseEvent.getX() + deltaX;
double newY = mouseEvent.getY() + deltaY;
if (newX > 0 && newX < getScene().getWidth())
{
xProperty.set(newX);
}
if (newY > 0 && newY < getScene().getHeight())
{
yProperty.set(newY);
}
}
});
}
//JavaFx Accessor and mutator
}
public class CustomRectangle extends Rectangle
{
private DoubleProperty topRightX;
private DoubleProperty topRightY;
private DoubleProperty btmLeftX;
private DoubleProperty btmLeftY;
private DoubleProperty btmRightX;
private DoubleProperty btmRightY;
private DoubleProperty customeWidth;
private DoubleProperty customHeight;
public CustomRectangle()
{
super();
init();
}
public CustomRectangle(double x, double y, double width, double height)
{
super(x, y, width, height);
init();
}
public CustomRectangle(double width, double height, Paint fill)
{
super(width, height, fill);
init();
}
public CustomRectangle(double width, double height)
{
super(width, height);
init();
}
private void init()
{
topRightX = new SimpleDoubleProperty();
topRightY = new SimpleDoubleProperty();
btmLeftX = new SimpleDoubleProperty();
btmLeftY = new SimpleDoubleProperty();
btmRightX = new SimpleDoubleProperty();
btmRightY = new SimpleDoubleProperty();
topRightX.addListener((observable, oldValue, newValue) ->
{
this.setWidth(this.getWidth() + (newValue.doubleValue() - oldValue.doubleValue()));
});
topRightY.addListener((observable, oldValue, newValue) ->
{
this.setY(newValue.doubleValue());
this.setHeight(this.getHeight() - (newValue.doubleValue() - oldValue.doubleValue()));
});
btmLeftX.addListener((observable, oldValue, newValue) ->
{
this.setX(newValue.doubleValue());
this.setWidth(this.getWidth() - (newValue.doubleValue() - oldValue.doubleValue()));
});
btmLeftY.addListener((observable, oldValue, newValue) ->
{
this.setY(newValue.doubleValue());
this.setHeight(this.getHeight() + (newValue.doubleValue() - oldValue.doubleValue()));
});
btmRightX.addListener((observable, oldValue, newValue) ->
{
this.setWidth(this.getWidth() + (newValue.doubleValue() - oldValue.doubleValue()));
});
btmRightY.addListener((observable, oldValue, newValue) ->
{
this.setHeight(this.getHeight() + (newValue.doubleValue() - oldValue.doubleValue()));
});
this.xProperty().addListener((observable, oldValue, newValue) ->
{
btmLeftX.set(newValue.doubleValue());
topRightX.set(newValue.doubleValue() + this.getWidth());
btmRightX.set(newValue.doubleValue() + this.getWidth());
});
this.yProperty().addListener((observable, oldValue, newValue) ->
{
btmLeftY.set(newValue.doubleValue() + this.getHeight());
topRightY.set(newValue.doubleValue());
btmRightY.set(newValue.doubleValue() + this.getHeight());
});
this.widthProperty().addListener((observable, oldValue, newValue) ->
{
topRightX.set(this.getX() + (newValue.doubleValue() - oldValue.doubleValue()));
btmRightX.set(this.getX() + (newValue.doubleValue() - oldValue.doubleValue()));
});
this.heightProperty().addListener((observable, oldValue, newValue) ->
{
btmLeftY.set(this.getY() + (newValue.doubleValue() - oldValue.doubleValue()));
btmRightY.set(this.getY() + (newValue.doubleValue() - oldValue.doubleValue()));
});
}
//JavaFx Accessor and Mutator
}
public class FeatureHelper
{
private static double orgSceneX;
private static double orgSceneY;
private static double orgTranslateX;
private static double orgTranslateY;
public static CustomRectangle createDraggableRectangle(double x, double y, double width, double height,
boolean resizeImage)
{
CustomRectangle rect = new CustomRectangle(x, y, width, height);
// top left resize handle:
Handler topLeftHandle = new Handler(rect.topLeftXProperty(), rect.topLeftYProperty());
// top right resize handle:
Handler topRightHandle = new Handler(rect.topRightXProperty(), rect.topRightYProperty());
// bottom left resize handle:
Handler btmLeftHandle = new Handler(rect.btmLeftXProperty(), rect.btmLeftYProperty());
// bottom right resize handle:
Handler btmRightHandle = new Handler(rect.btmRightXProperty(), rect.btmRightYProperty());
// force circles to live in same parent as rectangle:
rect.parentProperty().addListener((obs, oldParent, newParent) ->
{
for (Circle c : Arrays.asList(topLeftHandle, topRightHandle, btmLeftHandle, btmRightHandle))
{
if (newParent != null)
{
((Pane) newParent).getChildren().add(c);
}
}
});
rect.setOnMousePressed(event ->
{
event.consume();
orgSceneX = event.getSceneX();
orgSceneY = event.getSceneY();
Node p = ((Node) (event.getSource()));
orgTranslateX = p.getTranslateX();
orgTranslateY = p.getTranslateY();
});
rect.setOnMouseDragged(event ->
{
double offsetX = event.getSceneX() - orgSceneX;
double offsetY = event.getSceneY() - orgSceneY;
double newTranslateX = orgTranslateX + offsetX;
double newTranslateY = orgTranslateY + offsetY;
Node p = ((Node) (event.getSource()));
p.setTranslateX(newTranslateX);
p.setTranslateY(newTranslateY);
for(Circle circle : Arrays.asList(topLeftHandle, topRightHandle, btmLeftHandle, btmRightHandle))
{
circle.setTranslateX(newTranslateX);
circle.setTranslateY(newTranslateY);
}
});
return rect;
}
}
You should have a look at this nice Example from karakullukcuhuseyin -->
https://github.com/karakullukcuhuseyin/JavaFX-ImageCropper
He extended the Rectangle Class as well and is using his custom Rectangle to select an area in an Image.
Related
I'm having trouble rotating a shape in JavaFX. Here's some code in which I want the arrowhead to point in the same direction as the line (yes, I will be adding a translation later, to put it on the end of the line, but one thing at a time).
The println statement is showing me that theta is updating correctly as I move the start and end points around, but the arrowhead stays stubbornly at the original angle. Is there another step I have to do in order to get shape to rotate, or should I be doing it a different way?
package application.view;
public class Connector extends Group {
private final DoubleProperty startX;
private final DoubleProperty startY;
private final DoubleProperty endX;
private final DoubleProperty endY;
public Connector(Line line, Polygon arrowhead, Preferences prefs) {
arrowhead.setStroke(Color.BLACK);
arrowhead.setStrokeWidth(1);
arrowhead.setFill(Color.CORNFLOWERBLUE);
startX = new SimpleDoubleProperty();
startY = new SimpleDoubleProperty();
endX = new SimpleDoubleProperty();
endY = new SimpleDoubleProperty();
thetaBinding = new DoubleBinding() {
{
super.bind(startX, startY, endX, endY);
}
#Override
protected double computeValue() {
return Math.atan2(getEndY() - getStartY(), getEndX() - getStartX());
}
};
theta = new SimpleDoubleProperty();
theta.bind(thetaBinding);
theta.addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
System.out.println(newValue);
arrowhead.rotateProperty().set((Double)newValue);
}
});
lineLength = new DoubleBinding() {
{
super.bind(startX, startY, endX, endY);
}
#Override
protected double computeValue() {
return Math.hypot(getEndX() - getStartX(), getEndY() - getStartY()) - prefs.getDouble("CONNECTOR_ARROW_LENGTH", Defaults.CONNECTOR_ARROW_LENGTH);
}
};
lineEndX = new DoubleBinding() {
{
super.bind(startX, lineLength, theta);
}
#Override
protected double computeValue() {
return startX.get() + lineLength.get() * Math.cos(theta.get());
}
};
lineEndY = new DoubleBinding() {
{
super.bind(startY, lineLength, theta);
}
#Override
protected double computeValue() {
return startY.get() + lineLength.get() * Math.sin(theta.get());
}
};
arrowhead.getPoints().setAll(new Double[] {
0.0, 0.0,
0.0, prefs.getDouble("CONNECTOR_ARROW_HALF_WIDTH", Defaults.CONNECTOR_ARROW_HALF_WIDTH),
prefs.getDouble("CONNECTOR_ARROW_LENGTH", Defaults.CONNECTOR_ARROW_LENGTH), 0.0,
0.0, -prefs.getDouble("CONNECTOR_ARROW_HALF_WIDTH", Defaults.CONNECTOR_ARROW_HALF_WIDTH),
});
line.startXProperty().bind(startX);
line.startYProperty().bind(startY);
line.endXProperty().bind(lineEndX);
line.endYProperty().bind(lineEndY);
getChildren().addAll(line, arrowhead);
}
public final DoubleBinding thetaBinding;
public final DoubleProperty theta;
public final DoubleBinding lineLength;
public final DoubleBinding lineEndX;
public final DoubleBinding lineEndY;
public double getStartX() {
return startX.get();
}
public void setStartX(double value) {
startX.set(value);
}
public DoubleProperty startXProperty() {
return startX;
}
public double getStartY() {
return startY.get();
}
public void setStartY(double value) {
startY.set(value);
}
public DoubleProperty startYProperty() {
return startY;
}
public double getEndX() {
return endX.get();
}
public void setEndX(double value) {
endX.set(value);
}
public DoubleProperty endXProperty() {
return endX;
}
public double getEndY() {
return endY.get();
}
public void setEndY(double value) {
endY.set(value);
}
public DoubleProperty endYProperty() {
return endY;
}
}
How can I use lineargradient for Line in Javafx?
I tried this:
LinearGradient lg = new LinearGradient(...);
line.setFill(lg);
....
It doesn't work.
Based on this answer, you need to specify the gradient on absolute coordinates.
Something like this will work for any given ones:
LinearGradient linearGradient = new LinearGradient(x1, y1, x2, y2, false, CycleMethod.REFLECT, new Stop(0,Color.RED),new Stop(1,Color.GREEN));
line.setStroke(linearGradient);
Based on this answer, you can create a simple app to test it how it works when you move the anchor nodes around the scene:
private final DoubleProperty x1 = new SimpleDoubleProperty();
public final double getX1() { return x1.get(); }
public final void setX1(double value) { x1.set(value); }
public final DoubleProperty x1Property() { return x1; }
private final DoubleProperty x2 = new SimpleDoubleProperty();
public final double getX2() { return x2.get(); }
public final void setX2(double value) { x2.set(value); }
public final DoubleProperty x2Property() { return x2; }
private final DoubleProperty y1 = new SimpleDoubleProperty();
public final double getY1() { return y1.get(); }
public final void setY1(double value) { y1.set(value); }
public final DoubleProperty y1Property() { return y1; }
private final DoubleProperty y2 = new SimpleDoubleProperty();
public final double getY2() { return y2.get(); }
public final void setY2(double value) { y2.set(value); }
public final DoubleProperty y2Property() { return y2; }
private Line line;
#Override
public void start(Stage primaryStage) {
Group group = new Group();
primaryStage.setScene(new Scene(group, 400, 400));
x1.set(100);
y1.set(50);
x2.set(200);
y2.set(300);
line = new Line(x1.get(), y1.get(), x2.get(), y2.get());
line.startXProperty().bind(x1);
line.startYProperty().bind(y1);
line.endXProperty().bind(x2);
line.endYProperty().bind(y2);
line.setStrokeWidth(12);
line.setMouseTransparent(true);
Anchor start = new Anchor(Color.BLUE, x1, y1);
Anchor end = new Anchor(Color.YELLOW, x2, y2);
group.getChildren().setAll(line, start, end);
x1Property().addListener(o -> updateLine());
x2Property().addListener(o -> updateLine());
y1Property().addListener(o -> updateLine());
y2Property().addListener(o -> updateLine());
updateLine();
primaryStage.show();
}
private void updateLine() {
LinearGradient linearGradient = new LinearGradient(x1.get(), y1.get(), x2.get(), y2.get(), false, CycleMethod.REFLECT, new Stop(0,Color.RED),new Stop(1,Color.GREEN));
line.setStroke(linearGradient);
}
private class Anchor extends Circle {
Anchor(Color color, DoubleProperty x, DoubleProperty y) {
super(x.get(), y.get(), 10);
setFill(color.deriveColor(1, 1, 1, 0.5));
setStroke(color);
setStrokeWidth(2);
setStrokeType(StrokeType.OUTSIDE);
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(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(mouseEvent -> {
getScene().setCursor(Cursor.HAND);
});
setOnMouseDragged(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);
}
});
setOnMouseEntered(mouseEvent -> {
if (!mouseEvent.isPrimaryButtonDown()) {
getScene().setCursor(Cursor.HAND);
}
});
setOnMouseExited(mouseEvent -> {
if (!mouseEvent.isPrimaryButtonDown()) {
getScene().setCursor(Cursor.DEFAULT);
}
});
}
// records relative x and y co-ordinates.
private class Delta { double x, y; }
}
Use this
line.setStyle("-fx-background-color: linear-gradient(to right, #color_1, #color_2);");
I want to select few images available on my Canvas. How to select multiple of them using control key?
You create a model, e. g.
List<Node> selectionModel = new ArrayList<Node>();
and add a listener to each node, e. g.
imageView.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> {
if( event.isControlDown()) {
selectionModel.add( (Node) event.getSource());
// logging
System.out.println("Items in model:");
selectionModel.forEach(n -> System.out.println(n));
}
});
Of course you need to provide an option to clear the selection model, e. g. by clicking outside on the scene instead of an ImageView.
Here's a more sophisticated example. Supports rubberband selection, ctrl for selection toggle, shift for adding to selection and click on background to clear the selection.
public class ImageSelection extends Application {
Image image = new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/320px-Siberischer_tiger_de_edit02.jpg");
SelectionModel selectionModel = new SelectionModel();
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
double width = image.getWidth();
double height = image.getHeight();
double padding = 20;
for( int row=0; row < 4; row++) {
for( int col=0; col < 4; col++) {
ImageView imageView = new ImageView( image);
imageView.relocate( padding * (col+1) + width * col, padding * (row + 1) + height * row);
pane.getChildren().add(imageView);
}
}
Scene scene = new Scene( pane, 1800, 1200);
primaryStage.setScene( scene);
primaryStage.show();
new RubberBandSelection( pane);
}
public static void main(String[] args) {
launch(args);
}
private class SelectionModel {
Set<Node> selection = new HashSet<>();
public void add( Node node) {
node.setStyle("-fx-effect: dropshadow(three-pass-box, red, 10, 10, 0, 0);");
selection.add( node);
}
public void remove( Node node) {
node.setStyle("-fx-effect: null");
selection.remove( node);
}
public void clear() {
while( !selection.isEmpty()) {
remove( selection.iterator().next());
}
}
public boolean contains( Node node) {
return selection.contains(node);
}
public void log() {
System.out.println( "Items in model: " + Arrays.asList( selection.toArray()));
}
}
private class RubberBandSelection {
final DragContext dragContext = new DragContext();
Rectangle rect;
Pane group;
public RubberBandSelection( Pane group) {
this.group = group;
rect = new Rectangle( 0,0,0,0);
rect.setStroke(Color.BLUE);
rect.setStrokeWidth(1);
rect.setStrokeLineCap(StrokeLineCap.ROUND);
rect.setFill(Color.LIGHTBLUE.deriveColor(0, 1.2, 1, 0.6));
group.addEventHandler(MouseEvent.MOUSE_PRESSED, onMousePressedEventHandler);
group.addEventHandler(MouseEvent.MOUSE_DRAGGED, onMouseDraggedEventHandler);
group.addEventHandler(MouseEvent.MOUSE_RELEASED, onMouseReleasedEventHandler);
}
EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
dragContext.mouseAnchorX = event.getSceneX();
dragContext.mouseAnchorY = event.getSceneY();
rect.setX(dragContext.mouseAnchorX);
rect.setY(dragContext.mouseAnchorY);
rect.setWidth(0);
rect.setHeight(0);
group.getChildren().add( rect);
event.consume();
}
};
EventHandler<MouseEvent> onMouseReleasedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if( !event.isShiftDown() && !event.isControlDown()) {
selectionModel.clear();
}
for( Node node: group.getChildren()) {
if( node instanceof ImageView) {
if( node.getBoundsInParent().intersects( rect.getBoundsInParent())) {
if( event.isShiftDown()) {
selectionModel.add( node);
} else if( event.isControlDown()) {
if( selectionModel.contains( node)) {
selectionModel.remove( node);
} else {
selectionModel.add( node);
}
} else {
selectionModel.add( node);
}
}
}
}
selectionModel.log();
rect.setX(0);
rect.setY(0);
rect.setWidth(0);
rect.setHeight(0);
group.getChildren().remove( rect);
event.consume();
}
};
EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
double offsetX = event.getSceneX() - dragContext.mouseAnchorX;
double offsetY = event.getSceneY() - dragContext.mouseAnchorY;
if( offsetX > 0)
rect.setWidth( offsetX);
else {
rect.setX(event.getSceneX());
rect.setWidth(dragContext.mouseAnchorX - rect.getX());
}
if( offsetY > 0) {
rect.setHeight( offsetY);
} else {
rect.setY(event.getSceneY());
rect.setHeight(dragContext.mouseAnchorY - rect.getY());
}
event.consume();
}
};
private final class DragContext {
public double mouseAnchorX;
public double mouseAnchorY;
}
}
}
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
circle.centerXProperty().bind(...);
circle.centerYProperty().bind(...);
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:
import java.util.Arrays;
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Cursor;
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;
public class DraggingRectangle extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
Rectangle rect = createDraggableRectangle(200, 200, 400, 300);
rect.setFill(Color.NAVY);
root.getChildren().add(rect);
Scene scene = new Scene(root, 800, 800);
primaryStage.setScene(scene);
primaryStage.show();
}
private Rectangle createDraggableRectangle(double x, double y, double width, double height) {
final double handleRadius = 10 ;
Rectangle rect = new Rectangle(x, y, width, height);
// top left resize handle:
Circle resizeHandleNW = new Circle(handleRadius, Color.GOLD);
// bind to top left corner of Rectangle:
resizeHandleNW.centerXProperty().bind(rect.xProperty());
resizeHandleNW.centerYProperty().bind(rect.yProperty());
// bottom right resize handle:
Circle resizeHandleSE = new Circle(handleRadius, Color.GOLD);
// bind to bottom right corner of Rectangle:
resizeHandleSE.centerXProperty().bind(rect.xProperty().add(rect.widthProperty()));
resizeHandleSE.centerYProperty().bind(rect.yProperty().add(rect.heightProperty()));
// move handle:
Circle moveHandle = new Circle(handleRadius, Color.GOLD);
// bind to bottom center of Rectangle:
moveHandle.centerXProperty().bind(rect.xProperty().add(rect.widthProperty().divide(2)));
moveHandle.centerYProperty().bind(rect.yProperty().add(rect.heightProperty()));
// force circles to live in same parent as rectangle:
rect.parentProperty().addListener((obs, oldParent, newParent) -> {
for (Circle c : Arrays.asList(resizeHandleNW, resizeHandleSE, moveHandle)) {
Pane currentParent = (Pane)c.getParent();
if (currentParent != null) {
currentParent.getChildren().remove(c);
}
((Pane)newParent).getChildren().add(c);
}
});
Wrapper<Point2D> mouseLocation = new Wrapper<>();
setUpDragging(resizeHandleNW, mouseLocation) ;
setUpDragging(resizeHandleSE, mouseLocation) ;
setUpDragging(moveHandle, mouseLocation) ;
resizeHandleNW.setOnMouseDragged(event -> {
if (mouseLocation.value != null) {
double deltaX = event.getSceneX() - mouseLocation.value.getX();
double deltaY = event.getSceneY() - mouseLocation.value.getY();
double newX = rect.getX() + deltaX ;
if (newX >= handleRadius
&& newX <= rect.getX() + rect.getWidth() - handleRadius) {
rect.setX(newX);
rect.setWidth(rect.getWidth() - deltaX);
}
double newY = rect.getY() + deltaY ;
if (newY >= handleRadius
&& newY <= rect.getY() + rect.getHeight() - handleRadius) {
rect.setY(newY);
rect.setHeight(rect.getHeight() - deltaY);
}
mouseLocation.value = new Point2D(event.getSceneX(), event.getSceneY());
}
});
resizeHandleSE.setOnMouseDragged(event -> {
if (mouseLocation.value != null) {
double deltaX = event.getSceneX() - mouseLocation.value.getX();
double deltaY = event.getSceneY() - mouseLocation.value.getY();
double newMaxX = rect.getX() + rect.getWidth() + deltaX ;
if (newMaxX >= rect.getX()
&& newMaxX <= rect.getParent().getBoundsInLocal().getWidth() - handleRadius) {
rect.setWidth(rect.getWidth() + deltaX);
}
double newMaxY = rect.getY() + rect.getHeight() + deltaY ;
if (newMaxY >= rect.getY()
&& newMaxY <= rect.getParent().getBoundsInLocal().getHeight() - handleRadius) {
rect.setHeight(rect.getHeight() + deltaY);
}
mouseLocation.value = new Point2D(event.getSceneX(), event.getSceneY());
}
});
moveHandle.setOnMouseDragged(event -> {
if (mouseLocation.value != null) {
double deltaX = event.getSceneX() - mouseLocation.value.getX();
double deltaY = event.getSceneY() - mouseLocation.value.getY();
double newX = rect.getX() + deltaX ;
double newMaxX = newX + rect.getWidth();
if (newX >= handleRadius
&& newMaxX <= rect.getParent().getBoundsInLocal().getWidth() - handleRadius) {
rect.setX(newX);
}
double newY = rect.getY() + deltaY ;
double newMaxY = newY + rect.getHeight();
if (newY >= handleRadius
&& newMaxY <= rect.getParent().getBoundsInLocal().getHeight() - handleRadius) {
rect.setY(newY);
}
mouseLocation.value = new Point2D(event.getSceneX(), event.getSceneY());
}
});
return rect ;
}
private void setUpDragging(Circle circle, Wrapper<Point2D> mouseLocation) {
circle.setOnDragDetected(event -> {
circle.getParent().setCursor(Cursor.CLOSED_HAND);
mouseLocation.value = new Point2D(event.getSceneX(), event.getSceneY());
});
circle.setOnMouseReleased(event -> {
circle.getParent().setCursor(Cursor.DEFAULT);
mouseLocation.value = null ;
});
}
static class Wrapper<T> { T value ; }
}
TLDR: Wrap that for Loop in a newParent != null check.
I'm unable to comment because of low reputation points, but I'd like to point out something that needs to be added to James_D's code above to avoid a problem. I ran into issues when I attempted to use the clear() method on a Pane with multiple resizable rectangles. I was able to fix this by changing his code from...
This
// force circles to live in same parent as rectangle:
rect.parentProperty().addListener((obs, oldParent, newParent) -> {
for (Circle c : Arrays.asList(resizeHandleNW, resizeHandleSE, moveHandle)) {
Pane currentParent = (Pane)c.getParent();
if (currentParent != null) {
currentParent.getChildren().remove(c);
}
((Pane)newParent).getChildren().add(c);
}
});
To This.
// force circles to live in same parent as rectangle:
rect.parentProperty().addListener((obs, oldParent, newParent) -> {
if (newParent != null) {
for (Circle c : Arrays.asList(resizeHandleNW, resizeHandleSE, moveHandle)) {
Pane currentParent = (Pane)c.getParent();
if (currentParent != null) {
currentParent.getChildren().remove(c);
}
((Pane)newParent).getChildren().add(c);
}
}
});
I have a chart which I can zoom
final BarChart<String, Number> bc
= new BarChart<String, Number>(xAxis, yAxis);
final double SCALE_DELTA = 1.1;
bc.setOnScroll(new EventHandler<ScrollEvent>()
{
#Override
public void handle(ScrollEvent event)
{
event.consume();
if (event.getDeltaY() == 0)
{
return;
}
double scaleFactor = (event.getDeltaY() > 0) ? SCALE_DELTA : 1 / SCALE_DELTA;
bc.setScaleX(bc.getScaleX() * scaleFactor);
bc.setScaleY(bc.getScaleY() * scaleFactor);
}
});
I can zoom in and zoom out but I cannot drag the chart in order to navigate to specific zoom area. How I can drag the chart by clicking and holding the chart?
P.S I tested this code bit I don't get any result
label.setOnMousePressed(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
// record a delta distance for the drag and drop operation.
// treePane.x = label.getLayoutX() - mouseEvent.getSceneX();
treePane.setLayoutX(label.getLayoutX() - mouseEvent.getSceneX()); //= label.getLayoutX() - mouseEvent.getSceneX();
//treePane.y = label.getLayoutY() - mouseEvent.getSceneY();
treePane.setLayoutY(label.getLayoutY() - mouseEvent.getSceneY());
label.setCursor(Cursor.MOVE);
}
});
label.setOnMouseReleased(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
label.setCursor(Cursor.HAND);
}
});
label.setOnMouseDragged(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
label.setLayoutX(mouseEvent.getSceneX() + treePane.getLayoutX());
label.setLayoutY(mouseEvent.getSceneY() + treePane.getLayoutY());
}
});
label.setOnMouseEntered(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
label.setCursor(Cursor.HAND);
}
});
Any idea where I'm wrong?
You Can Use this code for drag and drog label This is solution for javafx
#FXML
public void lblDragMouseDragged(MouseEvent m)
{
System.out.println("Mouse Dragged");
System.out.println(lblDragTest.getLayoutX());
System.out.println(lblDragTest.getLayoutY());
int diffX;
int diffY;
int x = (int) (m.getX()+lblDragTest.getLayoutX()-rootAnchorPane.getLayoutX());
int y = (int) (m.getY()+lblDragTest.getLayoutY()-rootAnchorPane.getLayoutY());
if (y > 0 && x > 0 && y < rootAnchorPane.getHeight() && x < rootAnchorPane.getWidth())
{
lblDragTest.setLayoutX(x);
lblDragTest.setLayoutY(y);
}
}