如何延迟Drag-Start动态组合move和Drag-And-Drop [英] How to delay the Drag-Start to combine move and Drag-And-Drop dynamically
问题描述
我想提供一个应用程序:
I want to provide an application that:
- 允许移动图像(这里是一个矩形)
- 如果该对象被移出工作区域,则开始拖放以转移到其他应用程序
所以在对象在画布区域移动期间,javafx DragDetected()会过早,我禁止onDragDetected()处理,在onMouseDragged()处理程序中我尝试使用
So the javafx DragDetected() would come too soon during the object move on the canvas area, I suppress the onDragDetected() handling and in the onMouseDragged() handler I tried to convert the MouseDrag event into a Drag event using
event.setDragDetect(true);
但onDragDetected()再也不会.....我能做什么?
But the onDragDetected() comes never again..... what can I do?
完整的示例应用程序是:
The full sample application is:
package fx.samples;
import java.io.File;
import java.util.LinkedList;
import javax.imageio.ImageIO;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.WritableImage;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class DragRectangle extends Application {
Point2D lastXY = null;
public void start(Stage primaryStage) {
Pane mainPane = new Pane();
Scene scene = new Scene(mainPane, 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
Rectangle area = new Rectangle(0, 0, 500 , 500);
Rectangle rect = new Rectangle(0, 0, 30, 30);
rect.setFill(Color.RED);
mainPane.getChildren().add(rect);
rect.setOnMouseDragged(event -> {
System.out.println("Move");
Node on = (Node)event.getTarget();
if (lastXY == null) {
lastXY = new Point2D(event.getSceneX(), event.getSceneY());
}
double dx = event.getSceneX() - lastXY.getX();
double dy = event.getSceneY() - lastXY.getY();
on.setTranslateX(on.getTranslateX()+dx);
on.setTranslateY(on.getTranslateY()+dy);
lastXY = new Point2D(event.getSceneX(), event.getSceneY());
if (!area.intersects(event.getSceneX(), event.getSceneY(), 1, 1)) {
System.out.println("->Drag");
event.setDragDetect(true);
} else {
event.consume();
}
});
rect.setOnDragDetected(event -> {
System.out.println("Drag:"+event);
if (area.intersects(event.getSceneX(), event.getSceneY(), 1, 1)) { event.consume(); return; }
Node on = (Node)event.getTarget();
Dragboard db = on.startDragAndDrop(TransferMode.COPY);
db.setContent(makeClipboardContent(event, on, null));
event.consume();
});
rect.setOnMouseReleased(d -> lastXY = null);
}
public static ClipboardContent makeClipboardContent(MouseEvent event, Node child, String text) {
ClipboardContent cb = new ClipboardContent();
if (text != null) {
cb.put(DataFormat.PLAIN_TEXT, text);
}
if (!event.isShiftDown()) {
SnapshotParameters params = new SnapshotParameters();
params.setFill(Color.TRANSPARENT);
Bounds b = child.getBoundsInParent();
double f = 10;
params.setViewport(new Rectangle2D(b.getMinX()-f, b.getMinY()-f, b.getWidth()+f+f, b.getHeight()+f+f));
WritableImage image = child.snapshot(params, null);
cb.put(DataFormat.IMAGE, image);
try {
File tmpFile = File.createTempFile("snapshot", ".png");
LinkedList<File> list = new LinkedList<File>();
ImageIO.write(SwingFXUtils.fromFXImage(image, null),
"png", tmpFile);
list.add(tmpFile);
cb.put(DataFormat.FILES, list);
} catch (Exception e) {
}
}
return cb;
}
public static void main(String[] args) {
launch(args);
}
}
推荐答案
好的,我花了几个小时阅读JavaFX资源并使用EventDispatcher等播放它...最终很容易:
Ok, I spend some hours reading the JavaFX sources and playing arround with EventDispatcher etc... its finally easy:
简而言之:
在onMouseDragged()处理程序中禁止系统拖动开始提议并代表您设置该标志:
Suppress the system drag start proposal in the onMouseDragged() handler and set that flag on your behalf:
onMouseDragged(e -> {
e.setDragDetect(false); // clear the system proposal
if (...) e.setDragDetect(true); // trigger drag on your own decision
}
长文:
因此,启动DragDetected的机制是使用MouseEvent MOUSE_DRAGGED。系统拖动检测将应用一些规则来确定当前鼠标拖动是否会被解释为拖动,这里是原始代码:
The mechanism to start a DragDetected is consequently using the MouseEvent MOUSE_DRAGGED. The system Drag detection will apply some rules to determine if the current mouse-drag will be interpreted as a drag, here the original code:
if (dragDetected != DragDetectedState.NOT_YET) {
mouseEvent.setDragDetect(false);
return;
}
if (mouseEvent.getEventType() == MouseEvent.MOUSE_PRESSED) {
pressedX = mouseEvent.getSceneX();
pressedY = mouseEvent.getSceneY();
mouseEvent.setDragDetect(false);
} else if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED) {
double deltaX = Math.abs(mouseEvent.getSceneX() - pressedX);
double deltaY = Math.abs(mouseEvent.getSceneY() - pressedY);
mouseEvent.setDragDetect(deltaX > hysteresisSizeX ||
deltaY > hysteresisSizeY);
}
}
并设置
mouseEvent.setDragDetect(true)
$ b正常MOUSE_DRAG事件中的$ b
。该事件被传递下来并由所有'下行'EventDispatchers处理...只有当这个事件最终到达进行处理并且isDragDetect标志仍然为真时,才会生成后续DragDetected事件。
in the normal MOUSE_DRAG event. That event is passed down and is being processed by all 'down-chain' EventDispatchers... only if this events finally arrives for processing and if the isDragDetect flag is still true, a follow up DragDetected event will be generated.
所以我可以通过使用EventDispatcher清除isDragDetect标志来延迟DragDetected:
So I am able to delay the DragDetected by clearing the isDragDetect flag on its way down using an EventDispatcher:
mainPane.setEventDispatcher((event, chain) -> {
switch (event.getEventType().getName()) {
case "MOUSE_DRAGGED":
MouseEvent drag = (MouseEvent)event;
drag.setDragDetect(false);
if (!area.intersects(drag.getSceneX(), drag.getSceneY(), 1, 1)) {
System.out.println("->Drag down");
drag.setDragDetect(true);
}
break;
}
return chain.dispatchEvent(event);
});
如果此代码确定达到拖动条件,则只需设置标记。
And if this code decides that a drag condition is reached, it simply sets the flag.
drag.setDragDetect(true);
现在我可以准确移动我的对象并在他们被移动到应用程序区域之外时开始拖动。
Now I am able to move precisely my objects and start the Drag if they are moved outside the application area.
经过几分钟的思考后:没有必要使用EventDispatcher,所有这些都可以在onMouseDragged处理程序中完成...
And after some minutes of thinking: the EventDispatcher is not necessary, all can be done in the onMouseDragged handler...
完整代码:
package fx.samples;
import java.io.File;
import java.util.LinkedList;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.WritableImage;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
public class DragRectangle extends Application {
Point2D lastXY = null;
public void start(Stage primaryStage) {
Pane mainPane = new Pane();
Scene scene = new Scene(mainPane, 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
Rectangle area = new Rectangle(0, 0, 500 , 500);
Rectangle rect = new Rectangle(0, 0, 30, 30);
rect.setFill(Color.RED);
mainPane.getChildren().add(rect);
rect.setOnMouseDragged(event -> {
System.out.println("Move");
event.setDragDetect(false);
Node on = (Node)event.getTarget();
if (lastXY == null) {
lastXY = new Point2D(event.getSceneX(), event.getSceneY());
}
double dx = event.getSceneX() - lastXY.getX();
double dy = event.getSceneY() - lastXY.getY();
on.setTranslateX(on.getTranslateX()+dx);
on.setTranslateY(on.getTranslateY()+dy);
lastXY = new Point2D(event.getSceneX(), event.getSceneY());
if (!area.intersects(event.getSceneX(), event.getSceneY(), 1, 1)) event.setDragDetect(true);
event.consume();
});
rect.setOnDragDetected(event -> {
System.out.println("Drag:"+event);
Node on = (Node)event.getTarget();
Dragboard db = on.startDragAndDrop(TransferMode.COPY);
db.setContent(makeClipboardContent(event, on, "red rectangle"));
event.consume();
});
rect.setOnMouseReleased(d -> lastXY = null);
}
public static ClipboardContent makeClipboardContent(MouseEvent event, Node child, String text) {
ClipboardContent cb = new ClipboardContent();
if (text != null) {
cb.put(DataFormat.PLAIN_TEXT, text);
}
if (!event.isShiftDown()) {
SnapshotParameters params = new SnapshotParameters();
params.setFill(Color.TRANSPARENT);
Bounds b = child.getBoundsInParent();
double f = 10;
params.setViewport(new Rectangle2D(b.getMinX()-f, b.getMinY()-f, b.getWidth()+f+f, b.getHeight()+f+f));
WritableImage image = child.snapshot(params, null);
cb.put(DataFormat.IMAGE, image);
try {
File tmpFile = File.createTempFile("snapshot", ".png");
LinkedList<File> list = new LinkedList<File>();
ImageIO.write(SwingFXUtils.fromFXImage(image, null),
"png", tmpFile);
list.add(tmpFile);
cb.put(DataFormat.FILES, list);
} catch (Exception e) {
}
}
return cb;
}
public static void main(String[] args) {
launch(args);
}
}
这篇关于如何延迟Drag-Start动态组合move和Drag-And-Drop的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!