title: JavaFX ScrollPane update viewportBounds on scroll
body: <p>I have a very large image I'm trying to display in JavaFX. To do this, I've split the image into several smaller ones, and only loading/displaying the parts that are visible in my <code>ScrollPane</code>.</p>
  
  <p>To detect the area visible in the <code>ScrollPane</code>, I'm adding listener to <code>ScrollPane.viewportBounds</code>. However, the <code>viewportBounds</code> is only updated when I resize the window, but not when I scroll the scroll bars.</p>
  
  <p>If I'm to be scrolling around my large image, I need <code>viewportBounds</code> to be updated when I scroll the scroll bars as well. How do I do this?</p>
  
  <hr>
  
  <p>I have some test code below. Clicking the <code>Button</code> works, but not through the <code>ChangeListener</code> - <code>left</code> and <code>right</code> still remain unchanged using the <code>ChangeListener</code>.</p>
  
  <pre><code>import javafx.application.Application;
  import javafx.beans.value.ChangeListener;
  import javafx.beans.value.ObservableValue;
  import javafx.event.ActionEvent;
  import javafx.event.EventHandler;
  import javafx.geometry.Bounds;
  import javafx.scene.Scene;
  import javafx.scene.control.Button;
  import javafx.scene.control.ScrollPane;
  import javafx.scene.layout.VBox;
  import javafx.scene.paint.Color;
  import javafx.scene.shape.Rectangle;
  import javafx.stage.Stage;
  
  public class TestClass extends Application {
      @Override
      public void start(Stage stage) {
          VBox vbox = new VBox();
  
          ScrollPane scrollPane = new ScrollPane(new Rectangle(1000, 700, Color.GREEN));
          scrollPane.setPrefSize(500, 300);
  
          // Using a ChangeListener on viewportBounds doesn't work.
          scrollPane.viewportBoundsProperty().addListener(new ChangeListener&lt;Bounds&gt;() {
              @Override
              public void changed(ObservableValue&lt;? extends Bounds&gt; observable, Bounds oldBounds, Bounds bounds) {
                  int left = -1 * (int) bounds.getMinX();
                  int right = left + (int) bounds.getWidth();
                  System.out.println("hval:" + scrollPane.getHvalue() + " left:" + left + " right:" + right);
              }
          });
  
          // Neither does this.
          scrollPane.hvalueProperty().addListener(new ChangeListener&lt;Number&gt;() {
              @Override
              public void changed(ObservableValue&lt;? extends Number&gt; observable, Number oldValue, Number newValue) {
                  Bounds bounds = scrollPane.getViewportBounds();
                  int left = -1 * (int) bounds.getMinX();
                  int right = left + (int) bounds.getWidth();
                  System.out.println("hval:" + scrollPane.getHvalue() + " left:" + left + " right:" + right);
              }
          });
  
          // Clicking the button works.
          Button printButton = new Button("Print");
  
          printButton.setOnAction(new EventHandler&lt;ActionEvent&gt;() {
              @Override
              public void handle(ActionEvent event) {
                  Bounds bounds = scrollPane.getViewportBounds();
                  int left = -1 * (int) bounds.getMinX();
                  int right = left + (int) bounds.getWidth();
                  System.out.println("hval:" + scrollPane.getHvalue() + " left:" + left + " right:" + right);
                  event.consume();
              }
          });
  
          vbox.getChildren().addAll(scrollPane, printButton);
  
          Scene scene = new Scene(vbox, 640, 480);
          stage.setScene(scene);
  
          stage.show();
      }
  
      public static void main(String[] args) {
          launch(args);
      }
  }
  </code></pre>
  
  <p><strong>EDIT</strong> Updated test code.</p>