滚动时JavaFX ScrollPane更新viewportBounds

时间:2014-10-07 16:09:37

标签: javafx scroll viewport bounds

我有一个非常大的图像,我试图在JavaFX中显示。为此,我将图片拆分为几个较小的图片,并仅加载/显示我ScrollPane中可见的部分。

要检测ScrollPane中可见的区域,我将监听器添加到ScrollPane.viewportBounds。但是,viewportBounds仅在我调整窗口大小时更新,但在滚动滚动条时不会更新。

如果我要在我的大图像上滚动,我还需要在滚动滚动条时更新viewportBounds。我该怎么做?


我在下面有一些测试代码。使用Button点击ChangeListener无法通过left - rightChangeListener仍然保持不变。

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<Bounds>() {
            @Override
            public void changed(ObservableValue<? extends Bounds> 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<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> 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<ActionEvent>() {
            @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);
    }
}

编辑更新了测试代码。

1 个答案:

答案 0 :(得分:5)

viewportBounds只是视口的边界,即可滚动内容可见部分的边界。滚动时这些不会改变(但随着大小的改变,通常会随着窗口大小的改变而改变)。

要响应在视口中移动的滚动内容,您需要观察hvalueProperty的{​​{1}}和vvalueProperty。您可以使用相同的更改侦听器,只需对参数类型进行少量修改:

ScrollPane

请注意,滚动时,视口(当然)不会在其父级中移动;滚动窗格内容的可见部分会发生变化。我没有简单的方法来计算内容的可见部分:你只需要做一些数学运算。

(注意:可能有一种更简单的方法,但我无法看到它。)

API docs for ScrollPane

  

ScrollPane允许应用程序设置当前,最小和   用于在水平和中定位内容的最大值   垂直方向。这些值按比例映射到   layout节点的layoutBounds。

所以你必须解释一下,但这意味着

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
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) {
        ScrollPane scrollPane = new ScrollPane(new Rectangle(1000, 700, Color.GREEN));
        scrollPane.setPrefSize(500, 300);

        ChangeListener<Object> changeListener = new ChangeListener<Object>() {
            @Override
            public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object 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);
            }
        };
        scrollPane.viewportBoundsProperty().addListener(changeListener);
        scrollPane.hvalueProperty().addListener(changeListener);
        scrollPane.vvalueProperty().addListener(changeListener);

        Scene scene = new Scene(scrollPane, 640, 480);
        stage.setScene(scene);

        stage.show();
    }

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

其中(hvalue - hmin) / (hmax - hmin) = hoffset / freeHspace hminhvalue是滚动窗格属性值,hmax是内容水平移动的数量,hoffset是内容可以移动的总水平量。这是

freeHspace

(显然,如果freeHspace = contentWidth - viewportWidth &lt; = contentWidth,则无法进行水平滚动,viewportWidth必须为零。)

所以如果你想要水平偏移

hoffset

类似的公式适用于垂直偏移。

因此,您可以使用

替换上面的更改侦听器
hoffset = Math.max(0, contentWidth - viewportWidth) * (hvalue - hmin) / (hmax - hmin)