JavaFX:计算特定控件的滚动位置

JavaFX:计算特定控件的滚动位置,java,javafx,scroll,controls,bounds,Java,Javafx,Scroll,Controls,Bounds,我需要计算控件相对于滚动窗格内容高度的确切位置,以便能够设置滚动窗格的v值,但我的公式似乎还不完全正确 我创建了一个带有滚动窗格的场景,滚动窗格由一个目录和许多节组成,而标签则充当节标题: 目录 第一节 第二节 第1节: 同侧眼线 第2节: 同侧眼线 我使用onMouseReleased事件来处理鼠标单击目录中的节标签。现在我想通过设置scrollpane的v值,自动向下滚动到相关的节标签(变量“control”) 我通过以下逻辑计算控件的滚动位置: scrollPane.setVvalue(0

我需要计算控件相对于滚动窗格内容高度的确切位置,以便能够设置滚动窗格的v值,但我的公式似乎还不完全正确

我创建了一个带有滚动窗格的场景,滚动窗格由一个目录和许多节组成,而标签则充当节标题:

目录

  • 第一节
  • 第二节
  • 第1节:

    同侧眼线

    第2节:

    同侧眼线

    我使用onMouseReleased事件来处理鼠标单击目录中的节标签。现在我想通过设置scrollpane的v值,自动向下滚动到相关的节标签(变量“control”)

    我通过以下逻辑计算控件的滚动位置:

    scrollPane.setVvalue(0.0); // Reset previous vValue to start from the top
    Bounds uberPaneBounds = uberPane.localToScene(uberPane.getBoundsInLocal());
    Bounds controlBounds = control.localToScene(control.getBoundsInLocal());
    double vValue = controlBounds.getMaxY() / uberPaneBounds.getMaxY();
    
    uberPane是一个直接位于scrollpane下的VBox,其中包括不同的窗格。因此,剖面标签嵌套在不同层次上的不同窗格中

    不幸的是,vValue并不完全正确,并且在滚动窗格中的相关部分越往下越不正确。似乎还有一个随高度增加而增加的附加参数。我做了10个部分的测试(包括ToC):


    感谢您的帮助

    滚动窗格无法滚动到内容底部高于滚动窗格视口底部的点。因此,可以滚动的最大值是滚动窗格视口中节点的高度减去视口本身的高度

    请注意,您假设滚动值的范围为0到最大滚动偏移(以像素为单位)。除非您显式更改它,否则这可能是正确的,但实际上您应该将所需的值线性插值到滚动窗格的
    vmin
    vmax
    属性定义的范围内

    假设
    control
    是滚动窗格中显示的节点的子节点,则需要如下内容:

    double y = control.getBoundsInParent().getMinY();
    double scrollRange = scrollPane.getVmax() - scrollPane.getVmin() ;
    double scrollPixelRange = scrollPane.getContent().getBoundsInLocal().getHeight()
        - scrollPane.getViewportBounds().getHeight();
    double scrollOffset = y*scrollRange / scrollPixelRange ;
    scrollPane.setVvalue(scrollPane.getVmin() + scrollOffset);
    
    下面是一个完整的示例:

    import java.util.Random;
    
    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.scene.Scene;
    import javafx.scene.control.Hyperlink;
    import javafx.scene.control.Label;
    import javafx.scene.control.ScrollPane;
    import javafx.scene.layout.Background;
    import javafx.scene.layout.BackgroundFill;
    import javafx.scene.layout.CornerRadii;
    import javafx.scene.layout.Pane;
    import javafx.scene.layout.VBox;
    import javafx.scene.paint.Color;
    import javafx.stage.Stage;
    
    
    /**
     * JavaFX App
     */
    public class App extends Application {
        
        private final Random rng = new Random();
    
        @Override
        public void start(Stage stage) {
            VBox content = new VBox(5);
            VBox tableOfContents = new VBox(5);
            content.getChildren().add(tableOfContents);
            
            ScrollPane scrollPane = new ScrollPane(content);
    
            for (int i = 1 ; i <=5 ; i++) {
                createSection("Section "+i, scrollPane, content, tableOfContents);
            }
            
            Scene scene = new Scene(scrollPane);
            stage.setScene(scene);
            stage.show();
        }
        
        private void createSection(String title, ScrollPane scrollPane, Pane content, Pane tableOfContents) {
            Label sectionLabel = new Label(title);
            Hyperlink link = new Hyperlink(title);
            link.setOnAction(e -> {
                double y = sectionLabel.getBoundsInParent().getMinY();
                double scrollRange = scrollPane.getVmax() - scrollPane.getVmin() ;
                double scrollPixelRange = scrollPane.getContent().getBoundsInLocal().getHeight()
                    - scrollPane.getViewportBounds().getHeight();
                double scrollOffset = y*scrollRange / scrollPixelRange ;
                scrollPane.setVvalue(scrollPane.getVmin() + scrollOffset);
            });
            Hyperlink returnToTop = new Hyperlink("Return to top");
            returnToTop.setOnAction(e -> scrollPane.setVvalue(scrollPane.getVmin()));
            Pane section = new Pane();
            section.setPrefWidth(400);
            section.setPrefHeight(200+rng.nextInt(400));
            section.setBackground(new Background(new BackgroundFill(Color.LIGHTBLUE, CornerRadii.EMPTY, Insets.EMPTY)));
            tableOfContents.getChildren().add(link);
            content.getChildren().addAll(sectionLabel, section, returnToTop);
        }
        
    
        public static void main(String[] args) {
            launch();
        }
    
    }
    

    (这会将控件的本地坐标转换为场景坐标,然后将这些坐标转换为滚动窗格内容的坐标。)

    感谢@James_D的详细描述和示例!你让我开心:)
    double y = scrollPane.getContent().sceneToLocal(
        control.localToScene(control.getBoundsInLocal())).getMinY();