Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

correct way to move a node by dragging in javafx 2?

Tags:

java

javafx

I'm converting a Swing/Graphics2D app with a lot of custom painting to a JavaFX2 app. Although I absolutely love the new API, I seem to have a performance problem when painting an ellipse that I want to paint below the mouse cursor wherever the mouse is moved. When I move my mouse in a steady way, not ridicously fast, I notice the ellipse is always drawn a few centimeters behind on the mouse trail, and only catches up when I stop moving the cursor. This in a scenegraph with only a handful nodes. In my Swing app I didn't have that problem.

I'm wondering if this is the correct approach for drawing a shape where the mousecursor is?

import javafx.application.Application; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.SceneBuilder; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.shape.Ellipse; import javafx.scene.shape.EllipseBuilder; import javafx.stage.Stage;  public class TestApp extends Application { public static void main(String[] args) {     launch(args); }  @Override public void start(Stage primaryStage) throws Exception {     Pane p = new Pane();      final Ellipse ellipse = EllipseBuilder.create().radiusX(10).radiusY(10).fill(Color.RED).build();     p.getChildren().add(ellipse);      p.setOnMouseMoved(new EventHandler<MouseEvent>() {         public void handle(MouseEvent event) {             ellipse.setCenterX(event.getX());             ellipse.setCenterY(event.getY());         }     });      Scene scene = SceneBuilder.create().root(p).width(1024d).height(768d).build();     primaryStage.setScene(scene);      primaryStage.show(); } } 

Small update: I upgraded to JavaFX 2.2 and Java7u6 (on Windows 7 64bit), doesn't seem to make a difference though.

like image 325
Nicolas Mommaerts Avatar asked May 21 '12 08:05

Nicolas Mommaerts


People also ask

How do I drag and drop in JavaFX?

Moreover, drag-and-drop can be implemented between a JavaFX application and a third-party (native) application such as Windows Explorer or a desktop. A drag-and-drop gesture happens as follows: The user click a mouse button on a gesture source, drags the mouse, and releases the mouse button on a gesture target.

How do I move something in JavaFX?

Just click on the circle and drag it. The vehicles will follow it.

Can a node be placed in a scene JavaFX?

Node objects may be constructed and modified on any thread as long they are not yet attached to a Scene in a Window that is showing. An application must attach nodes to such a Scene or modify them on the JavaFX Application Thread.


2 Answers

Here is some code I use to allow a Label to be dragged around in a Pane. I don't notice any significant lag behind the mouse trail with it.

// allow the label to be dragged around. final Delta dragDelta = new Delta(); label.setOnMousePressed(new EventHandler<MouseEvent>() {   @Override public void handle(MouseEvent mouseEvent) {     // record a delta distance for the drag and drop operation.     dragDelta.x = label.getLayoutX() - mouseEvent.getSceneX();     dragDelta.y = 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() + dragDelta.x);     label.setLayoutY(mouseEvent.getSceneY() + dragDelta.y);   } }); label.setOnMouseEntered(new EventHandler<MouseEvent>() {   @Override public void handle(MouseEvent mouseEvent) {     label.setCursor(Cursor.HAND);   } });  . . .  // records relative x and y co-ordinates. class Delta { double x, y; } 

Here is a small complete example app using the above code.

Update The above example, will still lag the object being dragged behind the cursor when the objects being dragged are small.

An alternate approach is to use an ImageCursor comprising of a MousePointer superimposed over the an image representation of the node being dragged, then hide and show the actual node at the start and completion of the drag. This means that the node drag rendering will not lag the cursor (as the image representation of the node is now the cursor). However this approach does have drawbacks => there are restrictions on the size and format of ImageCursors, plus you need to convert your Node to an Image to place it in an ImageCursor, for which you may need advanced Node => Image conversion operations only to available in JavaFX 2.2+.

like image 142
jewelsea Avatar answered Sep 20 '22 00:09

jewelsea


The lag that you're describing (between your mouse and the dragged shape) is a known JavaFX bug:

https://bugs.openjdk.java.net/browse/JDK-8087922

You can work around it (on Windows, at least) by using an undocumented JVM flag:

-Djavafx.animation.fullspeed=true 

This flag is normally for internal performance testing, which is why it is undocumented, but we've been using it for months and haven't had any problems with it so far.

EDIT:

There's another, similar way to workaround this bug that might be a little easier on CPU usage. Simply turn off Prism's vertical sync:

-Dprism.vsync=false 

In our app, either of these workarounds solves the lag; there's no need to do both.

like image 21
Xanatos Avatar answered Sep 22 '22 00:09

Xanatos