Подтвердить что ты не робот

Правильный способ перемещения node путем перетаскивания в javafx 2?

Я конвертирую приложение 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 бит), похоже, не имеет никакого значения.

4b9b3361

Ответ 1

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

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

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

-Djavafx.animation.fullspeed=true

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

ИЗМЕНИТЬ:

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

-Dprism.vsync=false

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

Ответ 2

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

Ниже приведено небольшое полное приложение , используя приведенный выше код.

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

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

Ответ 3

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

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

Ответ 4

Есть ли это свойство cacheHint, доступное на всех узлах и которое может помочь?

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

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

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

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

Ответ 5

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

Ответ 6

вот код для перетаскивания метки с помощью мыши в 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);
    }
}

Ответ 7

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

Ответ 8

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