滚动时JavaFX ScrollPane更新viewportBounds [英] JavaFX ScrollPane update viewportBounds on scroll

查看:315
本文介绍了滚动时JavaFX ScrollPane更新viewportBounds的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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 ScrollPane.

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

To detect the area visible in the ScrollPane, I'm adding listener to ScrollPane.viewportBounds. However, the viewportBounds is only updated when I resize the window, but not when I scroll the scroll bars.

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

If I'm to be scrolling around my large image, I need viewportBounds to be updated when I scroll the scroll bars as well. How do I do this?

我在下面有一些测试代码。单击按钮有效,但不能通过 ChangeListener - 使用 ChangeListener 仍保持不变。

I have some test code below. Clicking the Button works, but not through the ChangeListener - left and right still remain unchanged using the ChangeListener.

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);
    }
}

编辑更新测试代码。

推荐答案

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

The viewportBounds are just the bounds of the viewport, i.e. the bounds of the portion of the scrollable content that is visible. These won't change as you scroll (but will usually change as you resize the window, as the size changes).

要响应在内部移动的滚动内容viewport,你需要观察 ScrollPane的 hvalueProperty vvalueProperty 。您可以使用相同的更改侦听器,只需对参数类型进行少量修改:

To respond to the scroll content being moved within the viewport, you need to observe the hvalueProperty and vvalueProperty of the ScrollPane. You can use the same change listener, with minor modifications to the types of the parameters:

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);
    }
}

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

Note that when you scroll, the viewport (of course) does not move in its parent; the visible portion of the scrollpane's content changes. There's no simple way I can see to compute the visible portion of the content: you just have to do a bit of maths.

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

(Note: there may be an easier way, but I am not able to see it.)

ScrollPane的API文档

The API docs for ScrollPane state


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

The ScrollPane allows the application to set the current, minimum, and maximum values for positioning the contents in the horizontal and vertical directions. These values are mapped proportionally onto the layoutBounds of the contained node.

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

So you have to interpret that a bit, but it means that

(hvalue - hmin) / (hmax - hmin) = hoffset / freeHspace

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

where hmin, hvalue, and hmax are scroll pane property values, hoffset is the amount by which the content is shifted horizontally, and freeHspace is the total horizontal amount the content can move. This is

freeHspace = contentWidth - viewportWidth

(显然,如果 contentWidth < = viewportWidth ,则无法进行水平滚动, hoffset 必然为零。)

(Obviously if contentWidth <= viewportWidth, no horizontal scrolling is possible, and hoffset is necessarily zero.)

所以如果你想要水平偏移

So if you want the horizontal offset you have

hoffset = Math.max(0, contentWidth - viewportWidth) * (hvalue - hmin) / (hmax - hmin)

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

And a similar formula holds for the vertical offset.

所以你可以用

    ChangeListener<Object> changeListener = new ChangeListener<Object>() {
        @Override
        public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
            double hmin = scrollPane.getHmin();
            double hmax = scrollPane.getHmax();
            double hvalue = scrollPane.getHvalue();
            double contentWidth = content.getLayoutBounds().getWidth();
            double viewportWidth = scrollPane.getViewportBounds().getWidth();

            double hoffset = 
                Math.max(0, contentWidth - viewportWidth) * (hvalue - hmin) / (hmax - hmin);

            double vmin = scrollPane.getVmin();
            double vmax = scrollPane.getVmax();
            double vvalue = scrollPane.getVvalue();
            double contentHeight = content.getLayoutBounds().getHeight();
            double viewportHeight = scrollPane.getViewportBounds().getHeight();

            double voffset = 
                Math.max(0,  contentHeight - viewportHeight) * (vvalue - vmin) / (vmax - vmin);

            System.out.printf("Offset: [%.1f, %.1f] width: %.1f height: %.1f %n", 
                    hoffset, voffset, viewportWidth, viewportHeight);
        }
    };

这篇关于滚动时JavaFX ScrollPane更新viewportBounds的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆