我想将一个元素作为父元素拖放到vbox上,并在拖放过程中显示节点移动,如何在最小的更改下完成此操作。
我想将一个元素作为父元素拖放到vbox上,并在拖放过程中显示节点移动,如何在最小的更改下完成此操作。
有关更多信息,请参见API文档。
简单示例(在JavaFX 8中代码更加简洁):
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.MouseDragEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
final VBox root = new VBox(5);
final ScrollPane scroller = new ScrollPane();
scroller.setContent(root);
final Scene scene = new Scene(scroller,400,200);
for (int i=1; i<=20; i++) {
final Label label = new Label("Item "+i);
addWithDragging(root, label);
}
// in case user drops node in blank space in root:
root.setOnMouseDragReleased(new EventHandler<MouseDragEvent>() {
@Override
public void handle(MouseDragEvent event) {
int indexOfDraggingNode = root.getChildren().indexOf(event.getGestureSource());
rotateNodes(root, indexOfDraggingNode, root.getChildren().size()-1);
}
});
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
private void addWithDragging(final VBox root, final Label label) {
label.setOnDragDetected(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
label.startFullDrag();
}
});
// next two handlers just an idea how to show the drop target visually:
label.setOnMouseDragEntered(new EventHandler<MouseDragEvent>() {
@Override
public void handle(MouseDragEvent event) {
label.setStyle("-fx-background-color: #ffffa0;");
}
});
label.setOnMouseDragExited(new EventHandler<MouseDragEvent>() {
@Override
public void handle(MouseDragEvent event) {
label.setStyle("");
}
});
label.setOnMouseDragReleased(new EventHandler<MouseDragEvent>() {
@Override
public void handle(MouseDragEvent event) {
label.setStyle("");
int indexOfDraggingNode = root.getChildren().indexOf(event.getGestureSource());
int indexOfDropTarget = root.getChildren().indexOf(label);
rotateNodes(root, indexOfDraggingNode, indexOfDropTarget);
event.consume();
}
});
root.getChildren().add(label);
}
private void rotateNodes(final VBox root, final int indexOfDraggingNode,
final int indexOfDropTarget) {
if (indexOfDraggingNode >= 0 && indexOfDropTarget >= 0) {
final Node node = root.getChildren().remove(indexOfDraggingNode);
root.getChildren().add(indexOfDropTarget, node);
}
}
public static void main(String[] args) {
launch(args);
}
}
setManaged(false)
,然后将其添加到VBox中。为VBox注册一个MouseListener,在鼠标拖动时调用relocate(...)
方法来移动ImageView。在dragReleased中,从VBox中删除ImageView和Listener。 - James_Droot.setOnMouseDragReleased
中执行它,那么它只会在释放发生在应用程序阈值内时才被应用。 - Arceus这是对@James_D优秀答案的补充说明。
这展示了如何按照@James_D在评论中建议的方式,向可拖动节点添加图像预览:
private void addPreview(final VBox root, final Label label) {
ImageView imageView = new ImageView(label.snapshot(null, null));
imageView.setManaged(false);
imageView.setMouseTransparent(true);
root.getChildren().add(imageView);
root.setUserData(imageView);
root.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
imageView.relocate(event.getX(), event.getY());
}
});
}
private void removePreview(final VBox root) {
root.setOnMouseDragged(null);
root.getChildren().remove(root.getUserData());
root.setUserData(null);
}
在label.setOnDragDetected()
中调用addPreview()
。在label.setOnMouseDragReleased()
和root.setOnMouseDragReleased()
中调用removePreview()
。
// Root is the node you want to drag, not the scene root.
root.setOnDragDetected(mouseEvent -> {
final ImageView preview = new ImageView(root.snapshot(null, null));
final Dragboard db = root.startDragAndDrop(TransferMode.ANY);
db.setContent( // Set your content to something here.
);
db.setDragView(preview.getImage());
mouseEvent.consume();
});