2012-05-21 49 views
17

Estoy convirtiendo una aplicación Swing/Graphics2D con mucha pintura personalizada en una aplicación JavaFX2. Aunque me encanta la nueva API, parece que tengo un problema de rendimiento al pintar una elipse que quiero pintar debajo del cursor del mouse donde sea que se mueva el mouse. Cuando muevo el mouse de manera firme, no ridículamente rápido, noto que la elipse siempre se dibuja unos centímetros atrás en el recorrido del mouse, y solo se levanta cuando dejo de mover el cursor. Esto en un scenegraph con solo un puñado de nodos. En mi aplicación Swing no tuve ese problema.forma correcta de mover un nodo arrastrando en javafx 2?

Me pregunto si este es el enfoque correcto para dibujar una forma donde está el mousecursor?

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(); 
} 
} 

Pequeña actualización: He actualizado a JavaFX 2.2 y Java7u6 (en Windows 7 64 bits), no parece hacer una diferencia sin embargo.

+0

Su solución en sí me ha ayudado mucho, gracias! – Matteo

+0

Me alegra que te haya ayudado :) –

Respuesta

6

El retraso que se está describiendo (entre el ratón y la forma arrastrado) es un error conocido de JavaFX:

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

se puede trabajar alrededor de ella (en Windows, por lo menos) mediante el uso de una JVM bandera indocumentado:

-Djavafx.animation.fullspeed=true 

Esta bandera es normalmente para las pruebas de rendimiento interno, por lo que es indocumentado, pero hemos estado usando durante meses y no han tenido ningún problema con ella hasta ahora.

EDIT:

Hay otro, de forma similar a solucionar este error que podría ser un poco más fácil en el uso de CPU. Simplemente apague la sincronización vertical de Prism:

-Dprism.vsync=false 

En nuestra aplicación, cualquiera de estas soluciones soluciona el retraso; no hay necesidad de hacer ambas cosas.

+1

increíble, después de todo este tiempo ... hace el truco de hecho –

+0

esto es increíble, muchas gracias por esto –

18

Aquí hay un código que uso para permitir que un Label se arrastre en un Pane. No noto ningún retraso significativo detrás del rastro del mouse con él.

// 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; } 

Aquí está una pequeña completa example app utilizando el código de seguridad.

Actualización El ejemplo anterior, todavía retrasará el objeto que se arrastra detrás del cursor cuando los objetos que se arrastran son pequeños.

Un enfoque alternativo es usar un ImageCursor que comprende un MousePointer superpuesto sobre la representación de una imagen del nodo que se está arrastrando, luego ocultar y mostrar el nodo real al inicio y al final del arrastre. Esto significa que la representación del arrastre del nodo no retrasará el cursor (ya que la representación de la imagen del nodo ahora es el cursor). Sin embargo, este enfoque tiene inconvenientes => hay restricciones en el tamaño y el formato de ImageCursors, además de que necesita convertir su Node en una Imagen para colocarlo en un ImageCursor, para lo cual puede necesitar Nodo avanzado => Operaciones de conversión de imagen solo para disponible en JavaFX 2.2+.

+0

Gracias por la elaborada respuesta, pero usando tu método, también recibo el rezago. ¡Extraño! –

+0

He agregado un ejemplo simple completo de lo que quiero decir. Incluso este pequeño programa muestra retraso al mover el mouse de manera constante. Tenga en cuenta que no quiero arrastrar la elipse, pero quiero mostrarla debajo del cursor del mouse incluso cuando solo mueve el mouse y no hace clic. –

+0

OK, cuando hice que los objetos se arrastraran lo suficientemente pequeños pude notar el mismo retraso con mi enfoque que veo en el código de muestra (simplemente se estaba enmascarando cuando el objeto que se arrastraba era más grande). – jewelsea

2

Existe esta propiedad "cacheint", disponible en todos los Nodos y que puede ayudar?

http://docs.oracle.com/javafx/2/api/javafx/scene/Node.html#cacheHintProperty

Bajo ciertas circunstancias, tales como la animación de nodos que son muy caros de hacer, es deseable ser capaz de realizar transformaciones en el nodo sin tener que regenerar el mapa de bits almacenado en caché. Una opción en tales casos es realizar las transformaciones en el mapa de bits almacenado en caché.

Esta técnica puede proporcionar una mejora dramática al rendimiento de la animación, aunque también puede dar lugar a una reducción en la calidad visual. La variable cacheHint proporciona una pista al sistema sobre cómo y cuándo se puede aceptar esa compensación (calidad visual para el rendimiento de la animación).

Si su elipse permanece igual todo el tiempo, pero se vuelve a dibujar cada vez que la mueve en un píxel, esto parece ser una gran ralentización.

+0

gracias, no parece hacer la diferencia, aunque –

3

Para mí, no se trata de una cuestión de rendimiento de la pintura, sino de cómo se genera la secuencia de eventos del mouse. Los eventos no se generan en tiempo real, algunos se omiten, cuando el mouse se mueve rápido. Para la mayoría de las aplicaciones, esta será la manera suficiente.El puntero del mouse se mueve en tiempo real sin ningún retraso de tiempo.

Si no desea este efecto, tendrá que escuchar el puntero del mouse directamente o buscar la forma de obtener los eventos en mayor densidad. No sé cómo yo mismo.

+1

Hmm, ¿tiene alguna documentación para respaldar esos reclamos? Lo encontraría raro teniendo en cuenta que puedes tomar un elemento en SceneBuilder, arrastra el mouse lo más rápido que puedas y se pegará constantemente al puntero del mouse. –

+0

Lo sentimos, no pudimos encontrar nada. Escribió una prueba, hubo alrededor de 10 milisegundos entre dos eventos en mi máquina. En SceneBuilder reconozco un retraso similar. –

+0

Creo que tiene razón, el problema está relacionado con MouseEvents.Hay un error en el repositorio JavaFX JIRA relacionado con esto: https://javafx-jira.kenai.com/browse/RT-34608 – Xanatos

2

Estaba teniendo el mismo problema al intentar hacer que los nodos de un gráfico se puedan arrastrar. Lo arreglé llamando al chart.setAnimated(false); En mi caso, el retraso fue causado por JavaFX aplicando una agradable animación suave a los cambios que estaba haciendo mi código.

+0

Creo que eso solo es aplicable a la tabla api –

1

Este es el código para arrastrar y soltar a través del sello del ratón en 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); 
    } 
} 
1

puede utilizar: Node.setCache (true); (lo uso con un panel con muchos niños como un campo de texto)

0

Arrastre Esfera

@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; 
    } 
} 
Cuestiones relacionadas