在 ScrollPane 视口中居中 GridPane 正方形
Center GridPane Square in ScrollPane Viewport
我有一个 20x20 GridPane
大小均匀的正方形,它表示来自相同大小的二维数组的数据,放置在 ScrollPane
中,这样就可以用它来平移. GridPane
的宽度和高度(以像素为单位)均远大于 ScrollPane
.
的相应尺寸
我的问题是我一直无法创建一个功能方法,使 ScrollPane
的视口在位于一组指定坐标的 GridPane
正方形上居中。
我对我一直试图利用的 scrollPane.setHvalue(double)
和 scrollPane.setVvalue(double)
的理解是,传递给它们的双精度值是它设置滚动到的轴的百分比。但是,当我编写代码时假设,它无法将视图集中在预期的正方形上,有时正方形甚至不在视口上的任何位置:
private void centerViewOn(double x, double y){
scrollPane.setHvalue(x/grid.getDimensionX());
scrollPane.setVvalue(y/grid.getDimensionY());
}
其中 grid
是 class 的一个实例,它与这个问题唯一相关的是它具有网格的尺寸,而 double x
和 double y
是要居中的正方形的 GridPane 中的 x,y 坐标。
我做错了什么,我该如何解决?
编辑:
根据 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);
}
滚动窗格的水平滚动值在 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
垂直滚动完全类似。
hmin
和 hmax
的默认值分别是 0
和 1
,所以如果您假设这些不会改变,您可以稍微简化一下.
所以我认为你最终得到
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);
}
注意并非所有单元格都可以滚动到中心:如果它们太靠近边缘,它们将尽可能靠近中心滚动,但滚动窗格不允许空白space(除非内容小于视口)。
完整示例如下:
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);
}
}
我有一个 20x20 GridPane
大小均匀的正方形,它表示来自相同大小的二维数组的数据,放置在 ScrollPane
中,这样就可以用它来平移. GridPane
的宽度和高度(以像素为单位)均远大于 ScrollPane
.
我的问题是我一直无法创建一个功能方法,使 ScrollPane
的视口在位于一组指定坐标的 GridPane
正方形上居中。
我对我一直试图利用的 scrollPane.setHvalue(double)
和 scrollPane.setVvalue(double)
的理解是,传递给它们的双精度值是它设置滚动到的轴的百分比。但是,当我编写代码时假设,它无法将视图集中在预期的正方形上,有时正方形甚至不在视口上的任何位置:
private void centerViewOn(double x, double y){
scrollPane.setHvalue(x/grid.getDimensionX());
scrollPane.setVvalue(y/grid.getDimensionY());
}
其中 grid
是 class 的一个实例,它与这个问题唯一相关的是它具有网格的尺寸,而 double x
和 double y
是要居中的正方形的 GridPane 中的 x,y 坐标。
我做错了什么,我该如何解决?
编辑: 根据 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);
}
滚动窗格的水平滚动值在 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
垂直滚动完全类似。
hmin
和 hmax
的默认值分别是 0
和 1
,所以如果您假设这些不会改变,您可以稍微简化一下.
所以我认为你最终得到
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);
}
注意并非所有单元格都可以滚动到中心:如果它们太靠近边缘,它们将尽可能靠近中心滚动,但滚动窗格不允许空白space(除非内容小于视口)。
完整示例如下:
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);
}
}