将项目拖放到其中时向下或向上滚动ScrollPane。

时间:2017-01-20 01:21:30

标签: javafx drag-and-drop scrollpane

当用户将某些内容拖到其边缘时,我希望向上或向下滚动ScrollPaneScrollPane内部会有一个VBox,也会在VBox内。

我认为我需要在setOnDragExited中添加一些东西。但具体到底是什么?

这是一个例子的最小程序:

package application;


import java.io.IOException;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;


import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) { 
        BorderPane root = new BorderPane();

        VBox outerBox = new VBox();
        outerBox.setMaxSize(700, 300);

        root.setCenter(outerBox);

        Label outerLabel = new Label("I am outside!");

        ScrollPane sp = new ScrollPane();
        outerBox.getChildren().addAll(outerLabel,sp);
        VBox innerBox = new VBox();
        //setting size bigger than ScrollPane's view.
        innerBox.setPrefSize(600, 600);

        sp.setContent(innerBox);

        Label dragMe = new Label("Drag me to  the edge of scroll pane! \n"+"or drop me in the scrollpane!");
        root.setTop(dragMe);

        dragMe.setOnDragDetected((MouseEvent event) ->{  
            Dragboard db = dragMe.startDragAndDrop(TransferMode.ANY); 
            db.setDragView(((Node) event.getSource()).snapshot(null, null));  

            ClipboardContent content = new ClipboardContent();           
            content.putString((dragMe.getText()));

            db.setContent(content);
            event.consume();      
        });

        sp.setOnDragOver((DragEvent event) ->{
        event.acceptTransferModes(TransferMode.MOVE);
            event.consume();          
        });

        sp.setOnDragEntered((DragEvent event) -> {     
        });   

        sp.setOnDragExited((DragEvent event) -> {
            System.out.println("-----Make the scrollpane scroll up or down depending on exiting on bottem or top------");
            event.consume();                      
        });

        sp.setOnDragDropped((DragEvent event) ->{
            Dragboard db = event.getDragboard();
            System.out.println(((VBox) sp.getContent()).getChildren().add(new Label(db.getString())));      
        });

        Scene scene = new Scene(root,1000,1000);
        primaryStage.setScene(scene);
        primaryStage.show();

    }
    public static void main(String[] args) {
        launch(args);
    }
}

2 个答案:

答案 0 :(得分:2)

在这里找到了这个答案: Want to trigger scroll when dragging node outside the visible area in ScrollPane

没有完全回答并且没有使用ScrollPane因此我认为我将我的工作/发现作为答案发布。

我发现你可以通过制作动画来实现这一目标:

private Timeline scrolltimeline = new Timeline();
....
scrolltimeline.setCycleCount(Timeline.INDEFINITE);

scrolltimeline.getKeyFrames()
     .add(new KeyFrame(Duration.millis(20), (ActionEvent) -> { dragScroll();}));

<强>最小

package application;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Main extends Application {
    private ScrollPane sp;
    private Timeline scrolltimeline = new Timeline();
    private double scrollVelocity = 0;

    boolean dropped;

    //Higher speed value = slower scroll.
    int speed = 200;

    @Override
    public void start(Stage primaryStage) throws Exception {
        BorderPane root = new BorderPane();

        sp = new ScrollPane();
        sp.setPrefSize(300, 300);

        VBox outer = new VBox(sp);

        VBox innerBox = new VBox();
        innerBox.setPrefSize(200,1000);

        sp.setContent(innerBox);

        root.setCenter(outer);

        Label dragMe = new Label("drag me to edge!\n"+"or drop me in scrollpane!");
        root.setTop(dragMe);

        setupScrolling();

        dragMe.setOnDragDetected((MouseEvent event) ->{  
            Dragboard db = dragMe.startDragAndDrop(TransferMode.ANY); 
            db.setDragView(((Node) event.getSource()).snapshot(null, null));  

            ClipboardContent content = new ClipboardContent();           
            content.putString((dragMe.getText()));

            db.setContent(content);
            event.consume();      
        });


        Scene scene = new Scene(root, 640, 480);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void setupScrolling() {
        scrolltimeline.setCycleCount(Timeline.INDEFINITE);
        scrolltimeline.getKeyFrames().add(new KeyFrame(Duration.millis(20), (ActionEvent) -> { dragScroll();}));

        sp.setOnDragExited((DragEvent event) -> {

            if (event.getY() > 0) {
                scrollVelocity = 1.0 / speed;
            }
            else {
                scrollVelocity = -1.0 / speed;
            }
            if (!dropped){
                scrolltimeline.play();
            }

        });

        sp.setOnDragEntered(event -> {
            scrolltimeline.stop();
            dropped = false;
        });
        sp.setOnDragDone(event -> {
            System.out.print("test");
            scrolltimeline.stop();
        });           
        sp.setOnDragDropped((DragEvent event) ->{      
            Dragboard db = event.getDragboard();
            ((VBox) sp.getContent()).getChildren().add(new Label(db.getString())); 
            scrolltimeline.stop();
            event.setDropCompleted(true);
            dropped = true;


        });

        sp.setOnDragOver((DragEvent event) ->{
            event.acceptTransferModes(TransferMode.MOVE);                            
            });


        sp.setOnScroll((ScrollEvent event)-> {
            scrolltimeline.stop();   
        });

        sp.setOnMouseClicked((MouseEvent)->{
            System.out.println(scrolltimeline.getStatus());

        });

    }
    private void dragScroll() {
        ScrollBar sb = getVerticalScrollbar();
        if (sb != null) {
            double newValue = sb.getValue() + scrollVelocity;
            newValue = Math.min(newValue, 1.0);
            newValue = Math.max(newValue, 0.0);
            sb.setValue(newValue);
        }
    }

    private ScrollBar getVerticalScrollbar() {
        ScrollBar result = null;
        for (Node n : sp.lookupAll(".scroll-bar")) {
            if (n instanceof ScrollBar) {
                ScrollBar bar = (ScrollBar) n;
                if (bar.getOrientation().equals(Orientation.VERTICAL)) {
                    result = bar;
                }
            }
        }        
        return result;
    }

    public static void main(String[] args) {
        launch(args);
    }

}

答案 1 :(得分:1)

JavaFX-8没有公共API将ScrollPane滚动到某个位置(https://bugs.openjdk.java.net/browse/JDK-8102126)并投票以获得此类API。

滚动到Java8中的某个位置(将在Java9中断!)的黑客攻击是获取ScrollPaneSkin类型的ScrollPane的外观并在那里调用onTraverse方法。

相关问题