У меня есть простое приложение JavaFX для панорамирования и масштабирования, как показано ниже. Функциональные возможности панорамирования и масштабирования работают отлично, но я также хотел бы иметь возможность перетаскивать круговой узел. У меня проблема в том, что сначала панель прокрутки получает все события мыши, поэтому я не могу назначить перетаскивание только на круг. Возможно ли иметь перетаскиваемую / масштабируемую полосу прокрутки, а также иметь возможность перетаскивать узел внутри панели?
Снимок экрана
Здесь используется код, который я использую :
package sample;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
private ScrollPane scrollPane = new ScrollPane();
private final DoubleProperty zoomProperty = new SimpleDoubleProperty(1.0d);
private final DoubleProperty deltaY = new SimpleDoubleProperty(0.0d);
private final Group group = new Group();
ImageView bigImageView = null;
PanAndZoomPane panAndZoomPane = null;
Pane featuresPane = new Pane();
@Override
public void start(Stage primaryStage) throws Exception{
bigImageView = new ImageView();
StackPane bigStackpane = new StackPane();
bigStackpane.getChildren().add(bigImageView);
bigStackpane.getChildren().add(featuresPane);
featuresPane.toFront();
featuresPane.setOpacity(.8);
Circle circle = new Circle();
circle.setCenterX(200);
circle.setCenterY(200);
circle.setRadius(100);
circle.setFill(Color.RED);
circle.setOnMouseClicked(e -> {
System.out.println("circle clicked");
});
featuresPane.getChildren().add(circle);
scrollPane.setPannable(true);
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
group.getChildren().add(bigImageView);
group.getChildren().add(bigStackpane);
panAndZoomPane = new PanAndZoomPane();
zoomProperty.bind(panAndZoomPane.myScale);
deltaY.bind(panAndZoomPane.deltaY);
panAndZoomPane.getChildren().add(group);
SceneGestures sceneGestures = new SceneGestures(panAndZoomPane);
scrollPane.setContent(panAndZoomPane);
panAndZoomPane.toBack();
scrollPane.addEventFilter( MouseEvent.MOUSE_CLICKED, sceneGestures.getOnMouseClickedEventHandler());
scrollPane.addEventFilter( MouseEvent.MOUSE_PRESSED, sceneGestures.getOnMousePressedEventHandler());
scrollPane.addEventFilter( MouseEvent.MOUSE_DRAGGED, sceneGestures.getOnMouseDraggedEventHandler());
scrollPane.addEventFilter( ScrollEvent.ANY, sceneGestures.getOnScrollEventHandler());
AnchorPane bigImageAnchorPane = new AnchorPane();
bigImageAnchorPane.getChildren().add(scrollPane);
Image image = new Image("https://i.imgur.com/8p1XBag.jpg");
bigImageView.setImage(image);
bigImageAnchorPane.setTopAnchor(scrollPane, 1.0d);
bigImageAnchorPane.setRightAnchor(scrollPane, 1.0d);
bigImageAnchorPane.setBottomAnchor(scrollPane, 1.0d);
bigImageAnchorPane.setLeftAnchor(scrollPane, 1.0d);
BorderPane root = new BorderPane(bigImageAnchorPane);
Label label = new Label("Pan and Zoom Test");
root.setTop(label);
Scene scene = new Scene(root, 1000, 1000);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
class PanAndZoomPane extends Pane {
public static final double DEFAULT_DELTA = 1.5d; //1.3d
DoubleProperty myScale = new SimpleDoubleProperty(1.0);
public DoubleProperty deltaY = new SimpleDoubleProperty(0.0);
private Timeline timeline;
public PanAndZoomPane() {
this.timeline = new Timeline(30);//60
// add scale transform
scaleXProperty().bind(myScale);
scaleYProperty().bind(myScale);
}
public double getScale() {
return myScale.get();
}
public void setScale( double scale) {
myScale.set(scale);
}
public void setPivot( double x, double y, double scale) {
// note: pivot value must be untransformed, i. e. without scaling
// timeline that scales and moves the node
timeline.getKeyFrames().clear();
timeline.getKeyFrames().addAll(
new KeyFrame(Duration.millis(100), new KeyValue(translateXProperty(), getTranslateX() - x)), //200
new KeyFrame(Duration.millis(100), new KeyValue(translateYProperty(), getTranslateY() - y)), //200
new KeyFrame(Duration.millis(100), new KeyValue(myScale, scale)) //200
);
timeline.play();
}
public double getDeltaY() {
return deltaY.get();
}
public void setDeltaY( double dY) {
deltaY.set(dY);
}
}
/**
* Mouse drag context used for scene and nodes.
*/
class DragContext {
double mouseAnchorX;
double mouseAnchorY;
double translateAnchorX;
double translateAnchorY;
}
/**
* Listeners for making the scene's canvas draggable and zoomable
*/
public class SceneGestures {
private DragContext sceneDragContext = new DragContext();
PanAndZoomPane panAndZoomPane;
public SceneGestures( PanAndZoomPane canvas) {
this.panAndZoomPane = canvas;
}
public EventHandler<MouseEvent> getOnMouseClickedEventHandler() {
return onMouseClickedEventHandler;
}
public EventHandler<MouseEvent> getOnMousePressedEventHandler() {
return onMousePressedEventHandler;
}
public EventHandler<MouseEvent> getOnMouseDraggedEventHandler() {
return onMouseDraggedEventHandler;
}
public EventHandler<ScrollEvent> getOnScrollEventHandler() {
return onScrollEventHandler;
}
private EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
sceneDragContext.mouseAnchorX = event.getX();
sceneDragContext.mouseAnchorY = event.getY();
sceneDragContext.translateAnchorX = panAndZoomPane.getTranslateX();
sceneDragContext.translateAnchorY = panAndZoomPane.getTranslateY();
}
};
private EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
panAndZoomPane.setTranslateX(sceneDragContext.translateAnchorX + event.getX() - sceneDragContext.mouseAnchorX);
panAndZoomPane.setTranslateY(sceneDragContext.translateAnchorY + event.getY() - sceneDragContext.mouseAnchorY);
event.consume();
}
};
/**
* Mouse wheel handler: zoom to pivot point
*/
private EventHandler<ScrollEvent> onScrollEventHandler = new EventHandler<ScrollEvent>() {
@Override
public void handle(ScrollEvent event) {
double delta = PanAndZoomPane.DEFAULT_DELTA;
double scale = panAndZoomPane.getScale(); // currently we only use Y, same value is used for X
double oldScale = scale;
panAndZoomPane.setDeltaY(event.getDeltaY());
if (panAndZoomPane.deltaY.get() < 0) {
scale /= delta;
} else {
scale *= delta;
}
double f = (scale / oldScale)-1;
double dx = (event.getX() - (panAndZoomPane.getBoundsInParent().getWidth()/2 + panAndZoomPane.getBoundsInParent().getMinX()));
double dy = (event.getY() - (panAndZoomPane.getBoundsInParent().getHeight()/2 + panAndZoomPane.getBoundsInParent().getMinY()));
panAndZoomPane.setPivot(f*dx, f*dy, scale);
event.consume();
}
};
/**
* Mouse click handler
*/
private EventHandler<MouseEvent> onMouseClickedEventHandler = new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if (event.getButton().equals(MouseButton.PRIMARY)) {
if (event.getClickCount() == 2) {
System.out.println("Image Layer Double Clicked...");
}else{
System.out.println("Image Layer Clicked...");
}
}
}
};
}
}