ScrollPane视口中的中心GridPane广场

时间:2016-11-15 22:53:14

标签: java javafx centering scrollpane gridpane

我有一个20x20 GridPane个均匀大小的正方形,它们表示来自相同大小的二维数组的数据,放在ScrollPane中,以便人们可以使用它来平移。 GridPane的宽度和高度(以像素为单位)都远大于ScrollPane的相应尺寸。

我的问题是,我无法制作一个功能性方法,将ScrollPane的视口居中于位于一组指定坐标的GridPane方格上。

我对我尝试使用的scrollPane.setHvalue(double)scrollPane.setVvalue(double)的理解是,传递给它们的double值是它设置滚动的轴的百分比。但是,当我编写假设的代码时,它无法将视图置于预期的正方形中心,有时甚至不在视口的任何位置使用正方形:

private void centerViewOn(double x, double y){
    scrollPane.setHvalue(x/grid.getDimensionX());
    scrollPane.setVvalue(y/grid.getDimensionY());
}

grid是一个类的实例,其唯一与此问题相关的是它具有网格的维度,double xdouble y是x,y坐标正方形的GridPane旨在居中。

我做错了什么,我该如何解决这个问题?

编辑: 根据James_D的建议,我将我所做的作为最小,完整和可验证的例子包括在内:

private ScrollPane scrollPane;
private GridPane gridPane;
private double dimensionX;
private double dimensionY;

@Override
public void start(Stage primaryStage) {
    scrollPane = new ScrollPane();
    gridPane   = new GridPane();
    dimensionX = 20;
    dimensionY = 20;

    Pane temp;
    for(int x = 0; x < dimensionX; ++x){
        for(int y = 0; y < dimensionY; ++y){
            temp = new Pane();
            temp.setPrefSize(100, 100);
            temp.setOnMouseClicked( e -> {
                centerViewOn(GridPane.getColumnIndex((Pane)e.getSource()), GridPane.getRowIndex((Pane)e.getSource()));
                ((Pane)e.getSource()).setStyle("-fx-background-color: blue");
            });
            gridPane.add(temp, x, y);
        }
    }

    gridPane.setGridLinesVisible(true);
    scrollPane.setContent(gridPane);

    primaryStage.setScene(new Scene(scrollPane));
    primaryStage.show();
}

private void centerViewOn(double x, double y){
    double viewportWidth    = scrollPane.getViewportBounds().getWidth();
    double maxHscrollPixels = gridPane.getWidth() - viewportWidth;
    double hscrollPixels    = viewportWidth / 2 - (x + 0.5) * dimensionX / 20;
    scrollPane.setHvalue(hscrollPixels / maxHscrollPixels);

    double viewportHeight   = scrollPane.getViewportBounds().getHeight();
    double maxVscrollPixels = gridPane.getHeight() - viewportHeight;
    double vscrollPixels    = viewportHeight / 2 - (y + 0.5) * dimensionY / 20;
    scrollPane.setVvalue(vscrollPixels / maxVscrollPixels);
}

1 个答案:

答案 0 :(得分:3)

滚动窗格的水平滚动值在scrollPane.getHmin()scrollPane.getHmax()之间变化。它们是以任意单位表示的,以像素为单位滚动的量在整个范围内进行线性插值。

可能的水平滚动量(以像素为单位)范围从零到(网格窗格宽度 - 滚动窗格视口宽度)。

所以你有

(hvalue - hmin) / (hmax - hmin) = scrollPixels / (gridPaneWidth - viewportWidth)

所以在得到一些代数之后

hvalue = hmin + (hmax - hmin) * scrollPixels / (gridPaneWidth - viewportWidth)

假设每列的宽度相同,并且有dimensionX列,x列的总宽度为x * gridPaneWidth / dimensionX像素。由于您要滚动到单元格的中心,请添加另一个半单元格,并且由于您希望单元格的中心位于视口的中心,因此减去viewportWidth / 2。所以你有:

hscrollPixels =  (x + 0.5) * gridPane.getWidth() / dimensionX - viewportWidth / 2

垂直滚动完全类似。

hminhmax的默认值分别为01,因此如果您认为这些不会发生变化,您可以稍微简化一下

所以我认为你最终会

private void centerViewOn(double x, double y){
    double viewportWidth    = scrollPane.getViewportBounds().getWidth();
    double maxHscrollPixels = gridPane.getWidth() - viewportWidth;
    double hscrollPixels    =  (x + 0.5) * gridPane.getWidth() / dimensionX - viewportWidth / 2;
    scrollPane.setHvalue(hscrollPixels / maxHscrollPixels);

    double viewportHeight   = scrollPane.getViewportBounds().getHeight();
    double maxVscrollPixels = gridPane.getHeight() - viewportHeight;
    double vscrollPixels    =  (y + 0.5) * gridPane.getHeight() / dimensionY - viewportHeight / 2;
    scrollPane.setVvalue(vscrollPixels / maxVscrollPixels);
}

请注意,并非所有单元格都可以滚动到中心:如果它们太靠近边缘,它们将尽可能靠近中心滚动,但滚动窗格不允许空格(除非内容是比视口小。)

以下是完整的示例:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class ScrollToCenter extends Application {

    private ScrollPane scrollPane;
    private GridPane gridPane;
    private double dimensionX;
    private double dimensionY;

    @Override
    public void start(Stage primaryStage) {
        scrollPane = new ScrollPane();
        gridPane   = new GridPane();
        dimensionX = 20;
        dimensionY = 20;

        for(int x = 0; x < dimensionX; ++x){
            for(int y = 0; y < dimensionY; ++y){
                Pane temp = new Pane();
                temp.setPrefSize(100, 100);
                temp.setOnMouseClicked( e -> {
                    centerViewOn(GridPane.getColumnIndex(temp), GridPane.getRowIndex(temp));
                    temp.setStyle("-fx-background-color: blue");
                });
                gridPane.add(temp, x, y);
            }
        }

        gridPane.setGridLinesVisible(true);
        scrollPane.setContent(gridPane);

        primaryStage.setScene(new Scene(scrollPane));
        primaryStage.show();
    }

    private void centerViewOn(double x, double y){
        double viewportWidth    = scrollPane.getViewportBounds().getWidth();
        double maxHscrollPixels = gridPane.getWidth() - viewportWidth;
        double hscrollPixels    =  (x + 0.5) * gridPane.getWidth() / dimensionX - viewportWidth / 2;
        scrollPane.setHvalue(hscrollPixels / maxHscrollPixels);

        double viewportHeight   = scrollPane.getViewportBounds().getHeight();
        double maxVscrollPixels = gridPane.getHeight() - viewportHeight;
        double vscrollPixels    =  (y + 0.5) * gridPane.getHeight() / dimensionY - viewportHeight / 2;
        scrollPane.setVvalue(vscrollPixels / maxVscrollPixels);
    }

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