簡體   English   中英

JavaFX ScrollPane更新滾動上的視口范圍

[英]JavaFX ScrollPane update viewportBounds on scroll

我試圖在JavaFX中顯示一個非常大的圖像。 為此,我將圖像分為幾個較小的圖像,並且僅加載/顯示了ScrollPane中可見的部分。

為了檢測ScrollPane可見的區域,我將偵聽器添加到ScrollPane.viewportBounds 但是, viewportBounds僅在調整窗口大小時更新,而在滾動滾動條時不會更新。

如果要在大圖像上滾動,則還需要在滾動滾動條時更新viewportBounds 我該怎么做呢?


我下面有一些測試代碼。 點擊Button ,而不是通過作品ChangeListener - leftright仍然使用不變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只是視口的邊界,即可見的可滾動內容部分的邊界。 這些在滾動時不會改變(但通常會在您調整窗口大小時隨着大小改變而改變)。

要響應在視口中移動的滾動內容,您需要觀察ScrollPanehvaluePropertyvvalueProperty 您可以使用相同的更改偵聽器,並對參數的類型進行較小的修改:

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

請注意,滾動時,視口(當然)不會在其父視窗中移動; 滾動窗格內容的可見部分將更改。 我沒有一種簡單的方法可以計算內容的可見部分:您只需要做一點數學即可。

(注意:也許有一種更簡單的方法,但是我看不到。)

ScrollPane狀態的API文檔

ScrollPane允許應用程序設置用於在水平和垂直方向上放置內容的當前值,最小值和最大值。 這些值按比例映射到所包含節點的layoutBounds上。

所以您必須對此稍作解釋,但這意味着

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

其中hminhvalue ,和hmax是滾動窗格屬性值, hoffset是由哪個內容被水平地移位量, freeHspace是總水平量的內容可以移動。 這是

freeHspace = contentWidth - viewportWidth

(顯然,如果contentWidth <= viewportWidth ,則不可能進行水平滾動,並且hoffset必須為零。)

因此,如果要水平偏移量

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

垂直偏移也適用類似的公式。

因此,您可以將上面的更改偵聽器替換為

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM