Вопрос по java – правильный способ перемещения узла путем перетаскивания в javafx 2?

21

Я преобразую приложение 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();
}
}

Small update: Я обновился до JavaFX 2.2 и Java7u6 (в Windows 7 64bit), хотя, похоже, ничего не изменилось.

Твое решение мне очень помогло, спасибо! Matteo
Я рад, что это помогло тебе Nicolas Mommaerts

Ваш Ответ

8   ответов
7

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

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

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

-Djavafx.animation.fullspeed=true

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

РЕДАКТИРОВАТЬ

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

-Dprism.vsync=false

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

удивительно, после всего этого времени ... действительно ли трюк Nicolas Mommaerts
это удивительно, большое спасибо за это Nicolas Martel
22

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

Спасибо за сложный ответ, но используя твой метод, я тоже получаю отставание. Weird Nicolas Mommaerts
Я добавил полный простой пример того, что я имею в виду. Даже эта небольшая программа отображает задержку при постоянном перемещении мыши. Обратите внимание, что я не хочу перетаскивать эллипс, но хочу показать его под курсором мыши, даже если просто двигать мышь, а не щелкать. Nicolas Mommaerts
OK, когда я сделал объекты достаточно маленькими, я мог заметить такую же задержку с моим подходом, который я вижу в вашем примере кода (он просто маскировался, когда объект, который перетаскивали, был больше). jewelsea
Я добавил схему альтернативного решения с использованием ImageCursor. jewelsea
Спасибо, я думал о том же, но это кажется таким громоздким. Я просто не могу перестать думать, что должен быть другой, более простой способ достижения нормальной производительности. Я собираюсь вернуться к этому через некоторое время .. Nicolas Mommaerts
3

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

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

Хмм, у тебя есть документы, подтверждающие эти претензии? Я нахожу это странным, учитывая, что вы можете взять элемент в SceneBuilder, перетащить мышь так быстро, как сможете, и он будет постоянно привязываться к указателю мыши. Nicolas Mommaerts
Извините, ничего не смог найти. Написал тест, между двумя событиями на моей машине было около 10 миллисекунд. В SceneBuilder я распознаю подобное отставание. Dieter Tremel
Я думаю, что вы правы, проблема связана с MouseEvents. В репозитории JavaFX JIRA есть ошибка, связанная с этим: Javafx-jira.kenai.com / просмотр / RT-34608 Xanatos
2

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

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

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

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

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

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

Я думаю, что это применимо только к графику API Nicolas Mommaerts
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

(я использую его с панелью со многими детьми, как 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;
    }
}

Похожие вопросы