Je suis en train de convertir une application Swing/Graphics2D contenant beaucoup de dessins personnalisés en une application JavaFX2. Bien que j'aime absolument la nouvelle API, il semble que le dessin d'une ellipse que je souhaite peindre en dessous du curseur de la souris me pose un problème de performances, quel que soit l'endroit où la souris est déplacée. Lorsque je déplace ma souris de manière stable, pas ridiculement rapide, je remarque que l’ellipse est toujours dessinée quelques centimètres en arrière sur le tracé de la souris et ne se rattrape que lorsque j’arrête de déplacer le curseur. Ceci dans un scenegraph avec seulement une poignée de noeuds. Dans mon application Swing, je n'avais pas ce problème.
Je me demande si c'est la bonne approche pour dessiner une forme où se trouve le curseur de souris?
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();
}
}
Petite mise à jour: J'ai mis à niveau vers JavaFX 2.2 et Java7u6 (sous Windows 7 64 bits), ne semble toutefois pas faire la différence.
Le décalage que vous décrivez (entre votre souris et la forme déplacée) est un bogue connu de JavaFX:
https://bugs.openjdk.Java.net/browse/JDK-8087922
Vous pouvez le contourner (au moins sous Windows) en utilisant un indicateur JVM non documenté:
-Djavafx.animation.fullspeed=true
Cet indicateur est normalement utilisé pour les tests de performance internes, raison pour laquelle il est non documenté, mais nous l'utilisons depuis des mois et nous n'avons jamais rencontré de problèmes avec.
MODIFIER:
Il existe une autre manière similaire de contourner ce bogue qui pourrait être un peu plus facile pour utiliser le processeur. Désactivez simplement la synchronisation verticale de Prism:
-Dprism.vsync=false
Dans notre application, l'une ou l'autre de ces solutions de contournement résout le retard; il n'y a pas besoin de faire les deux.
Voici un code que j’utilise pour permettre à un Label d’être traîné dans un Pane . Je ne remarque pas de retard significatif derrière la trace de la souris.
// 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; }
Voici un petit exemple complet exemple d'application utilisant le code ci-dessus.
Update L'exemple ci-dessus retarde toujours l'objet en train de glisser derrière le curseur lorsque les objets à déplacer sont petits.
Une autre approche consiste à utiliser un ImageCursor comprenant un MousePointer superposé à la représentation sous forme d'image du nœud déplacé, puis à masquer et afficher le nœud réel au début et à la fin du glissement. Cela signifie que le rendu glissé du nœud ne sera pas à la traîne du curseur (car la représentation de l'image du nœud est maintenant le curseur). Cependant, cette approche présente des inconvénients => il existe des restrictions sur la taille et le format d'ImageCursors. De plus, vous devez convertir votre nœud en image pour le placer dans un ImageCursor, pour lequel vous aurez peut-être besoin de Node avancé => disponible dans JavaFX 2.2+.
Pour moi, cela ne ressemble pas à une question de performance graphique, mais à la manière dont la séquence d'événements de souris est générée. Les événements ne sont pas générés en temps réel, certains sont ignorés lorsque la souris se déplace rapidement. Pour la plupart des applications, ce sera la manière suffisante. Le pointeur de la souris se déplace en temps réel sans décalage.
Si vous ne voulez pas cet effet, vous devrez écouter le pointeur de la souris directement ou trouver un moyen d’obtenir les événements avec une densité supérieure. Je ne sais pas comment moi-même.
J'avais le même problème en essayant de rendre les nœuds d'un graphique déplaçables. Je l'ai corrigé en appelant chart.setAnimated(false);
. Dans mon cas, le retard était dû à l'application d'une animation fluide par JavaFX aux modifications apportées par mon code.
Il y a cette propriété "cacheHint", disponible sur tous les nœuds et qui peut aider?
http://docs.Oracle.com/javafx/2/api/javafx/scene/Node.html#cacheHintProperty
Dans certaines circonstances, telles que l'animation de nœuds dont le rendu est très coûteux, il est souhaitable de pouvoir effectuer des transformations sur le nœud sans avoir à régénérer le bitmap mis en cache. Dans ce cas, une option consiste à effectuer les transformations sur le bitmap mis en cache lui-même.
Cette technique peut améliorer considérablement les performances d'animation, mais peut également entraîner une réduction de la qualité visuelle. La variable cacheHint indique au système comment et quand ce compromis (qualité visuelle pour les performances d'animation) est acceptable.
Si votre ellipse reste la même tout le temps, mais est redessinée chaque fois que vous la déplacez d'un pixel, cela semble être un ralentissement considérable.
voici le code pour glisser et déposer l'étiquette à l'aide de la souris dans javafx
@FXML
public void lblDragMousePressed(MouseEvent m)
{
System.out.println("Mouse is pressed");
prevLblCordX= (int) lblDragTest.getLayoutX();
prevLblCordY= (int) lblDragTest.getLayoutY();
prevMouseCordX= (int) m.getX();
prevMouseCordY= (int) m.getY();
}
//set this method on mouse released event for lblDrag
@FXML
public void lblDragMouseReleased(MouseEvent m)
{
System.out.println("Label Dragged");
}
// set this method on Mouse Drag event for lblDrag
@FXML
public void lblDragMouseDragged(MouseEvent m)
{
diffX= (int) (m.getX()- prevMouseCordX);
diffY= (int) (m.getY()-prevMouseCordY );
int x = (int) (diffX+lblDragTest.getLayoutX()-rootAnchorPane.getLayoutX());
int y = (int) (diffY+lblDragTest.getLayoutY()-rootAnchorPane.getLayoutY());
if (y > 0 && x > 0 && y < rootAnchorPane.getHeight() && x < rootAnchorPane.getWidth())
{
lblDragTest.setLayoutX(x);
lblDragTest.setLayoutY(y);
}
}
vous pouvez utiliser: Node.setCache (true); (je l’utilise avec un volet avec beaucoup d’enfants comme un TextField)
Drag Sphere
@Override
public void initialize(URL location, ResourceBundle resources) {
super.initialize(location, resources);
labelTableName.setText("Table Categories");
final PhongMaterial blueMaterial = new PhongMaterial();
blueMaterial.setDiffuseColor(Color.BLUE);
blueMaterial.setSpecularColor(Color.LIGHTBLUE);
final Sphere sphere = new Sphere(50);
sphere.setMaterial(blueMaterial);
final Measure dragMeasure = new Measure();
final Measure position = new Measure();
sphere.setOnMousePressed(mouseEvent -> {
dragMeasure.x = mouseEvent.getSceneX() - position.x;
dragMeasure.y = mouseEvent.getSceneY() - position.y;
sphere.setCursor(Cursor.MOVE);
});
sphere.setOnMouseDragged(mouseEvent -> {
position.x = mouseEvent.getSceneX() - dragMeasure.x;
position.y = mouseEvent.getSceneY() - dragMeasure.y;
sphere.setTranslateX(position.x);
sphere.setTranslateY(position.y);
});
sphere.setOnMouseReleased(mouseEvent -> sphere.setCursor(Cursor.HAND));
sphere.setOnMouseEntered(mouseEvent -> sphere.setCursor(Cursor.HAND));
bottomHeader.getChildren().addAll( sphere);
}
class Measure {
double x, y;
public Measure() {
x = 0; y = 0;
}
}