Steven Jeffries
Steven Jeffries

Reputation: 3582

JavaFX 8, how to get center location of Scrollpane's Viewport

Viewport Center

The Problem

I'm trying to figure out a way to get at which point in the content node the scroll pane's viewport is centered on.

To elaborate on the picture above, the big rectangle is the content (let's say a large image), and the small rectangle is the portion that is shown by the scroll pane. I'm trying to find x and y which would be coordinates from the top left of the content.

What I've Tried

My first thought was to use the getViewportBounds() method of the scroll pane and use its minX and maxX properties to determine the center x point:

Bounds b = scrollPane.getViewportBounds();
double centerX = (b.getMinX() + b.getMaxX()) / 2;
double centerY = (b.getMinY() + b.getMaxY()) / 2;

However, this doesn't work because these numbers are negative and don't seem to accurately describe the x and y I'm looking for anyways.

My next thought was to use the scroll pane's hValue and vValue to get the top left corner of the viewport relative to the content:

Bounds b = scrollPane.getViewportBounds();
double centerX = scrollPane.getHvalue() + b.getWidth() / 2;
double centerY = scrollPane.getVvalue() + b.getHeight() / 2;

This didn't work either though as the hValue and vValue seem to be way too large (when scrolled in only a few pixels, I'm getting numbers like 1600).

My Questions

I seem to have a fundamental misunderstanding of how the viewport works with a scroll pane.

What am I doing wrong here? Can someone explain where these numbers come from? How do I find x and y like in the picture above?

Upvotes: 2

Views: 1604

Answers (1)

fabian
fabian

Reputation: 82461

Let (x, y) be the be coordinates of the top, left point shown in the viewport. You can write this as

((contentWidth - viewportWidth) * hValueRel, (contentHeight - viewportHeight) * vValueRel)

vValueRel = vValue / vMax
hValueRel = hValue / hMax

This means assuming hmin and vmin remain 0 you can keep a circle in the center of like this:

// update circle position to be centered in the viewport
private void update() {
    Bounds viewportBounds = scrollPane.getViewportBounds();
    Bounds contentBounds = content.getBoundsInLocal();

    double hRel = scrollPane.getHvalue() / scrollPane.getHmax();
    double vRel = scrollPane.getVvalue() / scrollPane.getVmax();

    double x = Math.max(0, (contentBounds.getWidth() - viewportBounds.getWidth()) * hRel) + viewportBounds.getWidth() / 2;
    double y = Math.max(0, (contentBounds.getHeight() - viewportBounds.getHeight()) * vRel) + viewportBounds.getHeight() / 2;

    Point2D localCoordinates = content.parentToLocal(x, y);
    circle.setCenterX(localCoordinates.getX());
    circle.setCenterY(localCoordinates.getY());
}

private Circle circle;
private Pane content;
private ScrollPane scrollPane;

@Override
public void start(Stage primaryStage) {
    // create ui
    circle = new Circle(10);
    content = new Pane(circle);
    content.setPrefSize(4000, 4000);
    scrollPane = new ScrollPane(content);
    Scene scene = new Scene(scrollPane, 400, 400);

    // add listener to properties that may change
    InvalidationListener l = o -> update();
    content.layoutBoundsProperty().addListener(l);
    scrollPane.viewportBoundsProperty().addListener(l);
    scrollPane.hvalueProperty().addListener(l);
    scrollPane.vvalueProperty().addListener(l);
    scrollPane.hmaxProperty().addListener(l);
    scrollPane.vmaxProperty().addListener(l);
    scrollPane.hminProperty().addListener(l);
    scrollPane.vminProperty().addListener(l);

    primaryStage.setScene(scene);
    primaryStage.show();
}

Upvotes: 2

Related Questions