2012-05-21 142 views
17

Tôi đang chuyển đổi ứng dụng Swing/Graphics2D với nhiều bức vẽ tùy chỉnh cho ứng dụng JavaFX2. Mặc dù tôi hoàn toàn yêu thích API mới, nhưng dường như tôi gặp vấn đề về hiệu suất khi vẽ hình elip mà tôi muốn vẽ bên dưới con trỏ chuột ở bất kỳ nơi nào chuột được di chuyển. Khi tôi di chuyển con chuột của tôi một cách ổn định, không nhanh chóng lố bịch, tôi nhận thấy hình elip luôn luôn được vẽ một vài cm phía sau trên con đường chuột, và chỉ bắt kịp khi tôi ngừng di chuyển con trỏ. Điều này trong một scenegraph chỉ với một vài nút. Trong ứng dụng Swing của tôi, tôi không có vấn đề đó.cách chính xác để di chuyển một nút bằng cách kéo trong javafx 2?

Tôi tự hỏi liệu đây có phải là cách tiếp cận chính xác để vẽ hình dạng con trỏ chuột không?

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

cập nhật nhỏ: tôi nâng cấp lên JavaFX 2.2 và Java7u6 (trên Windows 7 64bit), dường như không tạo sự khác biệt mặc dù.

+0

giải pháp của bạn tự nó đã giúp tôi rất nhiều, cảm ơn bạn! – Matteo

+0

Tôi rất vui vì nó đã giúp bạn :) –

Trả lời

6

Các lag mà bạn đang mô tả (giữa chuột và hình dạng kéo) là một JavaFX lỗi được biết:

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

Bạn có thể làm việc xung quanh nó (trên Windows, ít nhất) bằng cách sử dụng một không có giấy tờ JVM cờ:

-Djavafx.animation.fullspeed=true 

cờ này là bình thường để thử nghiệm hiệu suất nội bộ, đó là lý do tại sao nó được cung cấp tài liệu, nhưng chúng tôi đã sử dụng nó trong nhiều tháng và không có bất kỳ vấn đề với nó cho đến nay.

EDIT:

Có cách khác, cách tương tự để giải quyết lỗi này có thể dễ dàng hơn một chút khi sử dụng CPU. Chỉ cần tắt đồng bộ hóa dọc của Prism:

-Dprism.vsync=false 

Trong ứng dụng của chúng tôi, một trong các cách giải quyết này giải quyết độ trễ; không cần phải làm cả hai.

+1

tuyệt vời, sau tất cả thời gian này ... mẹo này thực sự là –

+0

điều này thật tuyệt vời, cảm ơn rất nhiều vì điều này –

18

Dưới đây là một số mã tôi sử dụng để cho phép Label được kéo xung quanh trong một Pane. Tôi không nhận thấy bất kỳ sự tụt hậu đáng kể nào sau đường mòn chuột với nó.

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

Đây là một số hoàn chỉnh nhỏ example app sử dụng mã ở trên.

Cập nhật Ví dụ trên, sẽ vẫn làm chậm đối tượng đang được kéo phía sau con trỏ khi đối tượng được kéo nhỏ.

Cách tiếp cận khác là sử dụng ImageCursor bao gồm một con trỏ chuột được chồng lên trên biểu diễn hình ảnh của nút đang được kéo, sau đó ẩn và hiển thị nút thực tế khi bắt đầu và hoàn thành thao tác kéo. Điều này có nghĩa là việc hiển thị kéo nút sẽ không làm trễ con trỏ (khi biểu diễn hình ảnh của nút bây giờ là con trỏ). Tuy nhiên phương pháp này có nhược điểm => có những hạn chế về kích thước và định dạng của ImageCursors, cộng với bạn cần chuyển đổi Node của bạn thành Image để đặt nó vào ImageCursor, bạn có thể cần Node => Image conversion có sẵn trong JavaFX 2.2+.

+0

Cảm ơn câu trả lời phức tạp, nhưng bằng cách sử dụng phương pháp của bạn, tôi nhận được sự chậm trễ quá. Kỳ dị! –

+0

Tôi đã thêm một ví dụ đơn giản hoàn chỉnh về ý tôi. Ngay cả chương trình nhỏ này cũng hiển thị độ trễ khi di chuyển chuột theo cách ổn định. Lưu ý rằng tôi không muốn kéo hình elip, nhưng tôi muốn hiển thị nó bên dưới con trỏ chuột ngay cả khi chỉ di chuyển chuột và không nhấp chuột. –

+0

OK, khi tôi tạo các đối tượng kéo xung quanh đủ nhỏ, tôi có thể nhận thấy độ trễ tương tự với cách tiếp cận mà tôi thấy trong mã mẫu của bạn (nó chỉ bị che khi đối tượng được kéo lớn hơn). – jewelsea

2

Thuộc tính "cacheHint" này khả dụng trên tất cả các nút và điều đó có thể hữu ích?

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

Trong những trường hợp nhất định, chẳng hạn như hiệu ứng động nút mà rất tốn kém để render, đó là mong muốn để có thể thực hiện biến đổi trên các nút mà không cần phải tạo lại bitmap cache. Một tùy chọn trong các trường hợp như vậy là thực hiện các phép biến đổi trên chính bitmap được lưu trong bộ nhớ cache.

Kỹ thuật này có thể cải thiện đáng kể hiệu suất hoạt ảnh, mặc dù cũng có thể làm giảm chất lượng hình ảnh. Biến cacheHint cung cấp gợi ý cho hệ thống về cách thức và khi nào sự cân bằng đó (chất lượng hình ảnh cho hiệu suất hoạt hình) được chấp nhận.

Nếu hình elip của bạn vẫn giữ nguyên trong toàn bộ thời gian, nhưng được vẽ lại mỗi lần bạn di chuyển bằng một pixel, điều này có vẻ là một sự sụt giảm rất lớn.

+0

cảm ơn, dường như không tạo sự khác biệt mặc dù –

3

Đối với tôi, nó không giống như một câu hỏi về hiệu suất vẽ, nhưng cách chuỗi các sự kiện chuột được tạo ra. Các sự kiện không được tạo ra trong thời gian thực, một số bị bỏ qua, khi chuột di chuyển nhanh. Đối với hầu hết các ứng dụng này sẽ là cách sufficent.Con trỏ chuột di chuyển theo thời gian thực mà không bị trễ thời gian.

Nếu bạn không muốn hiệu ứng này, bạn sẽ phải nghe trực tiếp con trỏ chuột hoặc tìm cách để nhận các sự kiện ở mật độ cao hơn. Tôi không biết làm thế nào bản thân mình.

+1

Hmm, bạn có bất kỳ tài liệu nào để trả lại những khiếu nại đó không? Tôi muốn tìm thấy nó kỳ lạ xem xét bạn có thể mất một yếu tố trong SceneBuilder, kéo chuột của bạn xung quanh nhanh như bạn có thể và nó sẽ liên tục dính vào con trỏ chuột. –

+0

Xin lỗi, không thể tìm thấy bất cứ điều gì. Đã viết một bài kiểm tra, có khoảng 10 phần nghìn giây giữa hai sự kiện trên máy của tôi. Trong SceneBuilder, tôi nhận ra một độ trễ tương tự. –

+0

Tôi nghĩ bạn đúng, vấn đề có liên quan đến MouseEvents.Có một lỗi trong kho lưu trữ JavaFX JIRA liên quan đến điều này: https://javafx-jira.kenai.com/browse/RT-34608 – Xanatos

2

Tôi gặp sự cố tương tự khi cố gắng tạo các nút trên biểu đồ có thể kéo được. Tôi đã sửa nó bằng cách gọi chart.setAnimated(false); Trong trường hợp của tôi, độ trễ đã được gây ra bởi JavaFX áp dụng một hình động mượt mà cho những thay đổi mà mã của tôi đã tạo ra.

+0

Tôi nghĩ rằng điều đó chỉ áp dụng cho biểu đồ api –

1

đây là đoạn code để kéo và thả nhãn sử dụng chuột trong 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

bạn có thể sử dụng: Node.setCache (true); (i sử dụng nó với một Pane với nhiều trẻ em như một TextField)

0

Kéo 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; 
    } 
} 
Các vấn đề liên quan