2012-05-21 2 views
17

Я конвертирую приложение Swing/Graphics2D с большим количеством пользовательской графики в приложение JavaFX2. Хотя я абсолютно люблю новый API, у меня, похоже, проблема с производительностью при рисовании эллипса, который я хочу нарисовать под курсором мыши, куда бы мышь не перемещалась. Когда я перемещаю мышь устойчивым образом, не смехотворно быстро, я замечаю, что эллипс всегда нарисован на несколько сантиметров позади тропы мыши и только догоняет, когда я перестаю перемещать курсор. Это в сценарии с несколькими узлами. В моем приложении Swing у меня не было этой проблемы.правильный способ перемещения узла путем перетаскивания в javafx 2?

Мне интересно, если это правильный подход для рисования фигуры, в которой находится мышеловка?

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

сам Ваше решение помогло мне, спасибо! – Matteo

+0

Я рад, что это помогло вам :) –

ответ

6

лаг, что вы описываете (между мышью и перемещенной формой) является известным JavaFX ошибка:

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

Вы можете работать вокруг него (на Windows, по крайней мере), используя недокументированные флаг виртуальной машины Java:

-Djavafx.animation.fullspeed=true 

Этот флаг обычно для внутреннего тестирования производительности, поэтому он не документирован, но мы использовали его в течение нескольких месяцев, и не было никаких проблем с ним до сих пор.

EDIT:

Там другой, подобный способ обойти эту ошибку, которая может быть немного легче на использование процессора. Просто выключите вертикальную синхронизацию Призма:

-Dprism.vsync=false 

В нашем приложении любое из этих обходных решений решает проблему задержки; нет необходимости делать то и другое.

+1

удивительно, ведь все это время ... делает трюк действительно –

+0

это потрясающе, спасибо за это –

18

Вот некоторый код, который я использую, чтобы разрешить перемещение 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 используя код выше.

Обновление Приведенный выше пример по-прежнему будет задерживать объект, перетаскиваемый за указатель, когда объекты, перетаскиваемые, являются небольшими.

Альтернативный подход заключается в использовании ImageCursor, состоящего из MousePointer, наложенного на представление изображения перетаскиваемого узла, затем скрыть и показать фактический узел при начале и завершении перетаскивания. Это означает, что рендеринг перетаскивания узла не будет отставать от курсора (поскольку представление изображения узла теперь является курсором). Однако этот подход имеет недостатки => существуют ограничения на размер и формат ImageCursors, плюс вам нужно преобразовать свой Node в изображение, чтобы поместить его в ImageCursor, для которого вам может потребоваться продвинутый Node => операции преобразования изображений только для доступный в JavaFX 2.2+.

+0

Спасибо за подробный ответ, но, используя ваш метод, я тоже получаю отставание. Weird! –

+0

Я добавил полный простой пример того, что я имею в виду. Даже эта небольшая программа демонстрирует отставание при постоянном перемещении мыши. Обратите внимание, что я не хочу перетаскивать эллипс, но я хочу показать его под курсором мыши, даже когда вы просто перемещаете мышь и не нажимаете. –

+0

ОК, когда я делал объекты достаточно маленькими, я мог заметить одно и то же отставание от моего подхода, который я вижу в вашем примере кода (он просто маскировался, когда объект перетаскивался больше). – jewelsea

2

Это свойство «cacheHint» доступно на всех узлах, и это может помочь?

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

При определенных обстоятельствах, например, анимировать узлы, которые являются очень дорогими для визуализации, то желательно, чтобы иметь возможность выполнять преобразования на узел без необходимости регенерации кэшированных растровых изображений. Опцией в таких случаях является выполнение преобразований на самом кэшированном растровом изображении.

Этот метод может обеспечить резкое улучшение производительности анимации, хотя также может привести к снижению визуального качества. Переменная cacheHint предоставляет подсказку системе о том, как и когда этот компромисс (визуальное качество для производительности анимации) является приемлемым.

Если ваш эллипс остается неизменным все время, но перерисовывается каждый раз, когда вы перемещаете его на один пиксель, это, по-видимому, является огромным замедлением.

+0

спасибо, кажется, не имеет значения, хотя –

3

Для меня это не похоже на вопрос о производительности живописи, но как генерируется последовательность событий мыши. События не генерируются в реальном времени, некоторые пропускаются, когда мышь движется быстро. Для большинства приложений это будет достаточно.Указатель мыши перемещается в реальном времени без какого-либо отставания.

Если вы не хотите этого эффекта, вам придется напрямую прислушаться к указателю мыши или найти способ получить события в более высокой плотности. Я не знаю, как себя.

+1

Хм, есть ли у вас документация, подтверждающая эти претензии? Я бы счел это странным, учитывая, что вы можете взять элемент в SceneBuilder, перетаскивайте мышью так быстро, как можете, и он будет постоянно придерживаться указателя мыши. –

+0

К сожалению, ничего не найдено. Написал тест, между двумя событиями на моей машине было около 10 миллисекунд. В SceneBuilder я обнаруживаю подобное отставание. –

+0

Я думаю, что вы правы, проблема связана с MouseEvents.В репозитории JavaFX JIRA есть ошибка: https://javafx-jira.kenai.com/browse/RT-34608 – Xanatos

2

У меня была такая же проблема, пытаясь сделать узлы на диаграмме перетаскиваемой. Я исправил это, вызвав chart.setAnimated(false);. В моем случае отставание вызвано тем, что JavaFX применяет приятную плавную анимацию к изменениям, которые мой код делает.

+0

Я думаю, что это применимо только к диаграмме api –

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

вы можете использовать: Node.setCache (истинный); (я использую его с панелью с большим количеством детей, как в TextField)

0

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; 
    } 
} 
Смежные вопросы