我正在将带有大量自定义绘画的Swing / Graphics2D应用程序转换为JavaFX2应用程序。虽然我非常喜欢新的API,但是在绘制椭圆时我似乎有一个性能问题,我希望在鼠标光标下方的任何地方都可以移动鼠标。当我以稳定的方式移动我的鼠标,而不是快速的滑动时,我注意到椭圆总是在鼠标轨迹后面几厘米处被绘制,并且只有在我停止移动光标时才会捕获。这在一个只有极少数节点的场景图中。在我的Swing应用程序中,我没有遇到这个问题。
我想知道这是否是绘制鼠标光标所在的形状的正确方法?
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();
}
}
小更新:我升级到JavaFX 2.2和Java7u6(在Windows 7 64位上),但似乎没有什么区别。
答案 0 :(得分:22)
以下是一些代码,用于在Label中拖动Pane。 我没有注意到跟踪鼠标的任何明显滞后。
// 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; }
使用上面的代码,这是一个小的完整example app。
<强>更新强> 当拖动的对象很小时,上面的例子仍然会滞后于拖动光标的对象。
另一种方法是使用包含MousePointer的ImageCursor叠加在被拖动节点的图像表示上,然后隐藏并显示拖动开始和完成时的实际节点。这意味着节点拖动渲染不会滞后于光标(因为节点的图像表示现在是光标)。然而,这种方法确实有缺点=&gt; ImageCursors的大小和格式有限制,另外您需要将节点转换为图像以将其放置在ImageCursor中,您可能需要高级Node =&gt;图像转换操作仅适用于JavaFX 2.2 +。
答案 1 :(得分:7)
您描述的滞后(在鼠标和拖动的形状之间)是一个已知的JavaFX错误:
https://bugs.openjdk.java.net/browse/JDK-8087922
您可以使用未记录的JVM标志解决它(至少在Windows上):
-Djavafx.animation.fullspeed=true
此标志通常用于内部性能测试,这就是为什么它没有文档记录,但我们已经使用了几个月,到目前为止还没有任何问题。
编辑:
还有另一种类似的方法来解决这个可能在CPU使用上更容易的错误。只需关闭Prism的垂直同步:
-Dprism.vsync=false
在我们的应用程序中,这些变通方法中的任何一个都解决了滞后问题。没有必要同时做这两件事。
答案 2 :(得分:3)
对我而言,它看起来不像绘画表现的问题,而是如何生成鼠标事件的序列。当鼠标快速移动时,事件不会实时生成,有些会被跳过。对于大多数应用来说,这将是足够的方式。鼠标指针实时移动,没有任何时间延迟。
如果你不想要这种效果,你必须直接听鼠标指针或找到一种方法来获得更高密度的事件。我不知道自己是怎么回事。
答案 3 :(得分:2)
这个“cacheHint”属性可以在所有节点上使用,这可能会有所帮助吗?
http://docs.oracle.com/javafx/2/api/javafx/scene/Node.html#cacheHintProperty
在某些情况下,例如动画渲染非常昂贵的节点,希望能够在节点上执行转换而无需重新生成缓存的位图。在这种情况下,一个选项是在缓存的位图本身上执行转换。
此技术可以显着改善动画效果,但也可能导致视觉质量下降。 cacheHint变量向系统提供有关如何以及何时权衡(动画性能的视觉质量)是否可接受的提示。
如果您的椭圆在整个时间内保持不变,但每次将其移动一个像素时都会重绘,这似乎是一个巨大的减速。
答案 4 :(得分:2)
在尝试使图表上的节点可拖动时,我遇到了同样的问题。我通过调用chart.setAnimated(false);
来修复它。在我的情况下,滞后是由JavaFX对我的代码所做的更改应用一个漂亮的平滑动画引起的。
答案 5 :(得分:1)
这是在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);
}
}
答案 6 :(得分:1)
你可以使用:Node.setCache(true); (我使用带有许多孩子的窗格,如TextField)
答案 7 :(得分:0)
拖动球体
@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;
}
}