如何将 javafx 8 滚动窗格的内容居中

How to center the content of a javafx 8 scrollpane

我有一个 ScrollPane,它包含一个 GridPane,它包含一个 ImageView,它包含一个图像。我想要的是让图像在 GridPane 中居中。

在没有 ScrollPane 的情况下,我可以使用 setAlignment(Pos.TOP_CENTER) 来完成此操作,但是一旦我将 GridPane 添加到 ScrollPane,图像就会显示在左上角。

如何让 ScrollPane 使用 setAlignment() 值或手动将图像居中?

---示例编辑---

没有滚动窗格的代码:

public class ImageDB extends Application
{
  @Override
  public void start(Stage root)
  {
    Image image = new Image("0.jpg");
    ImageView view = new ImageView();
    view.setImage(image);

    GridPane grid = new GridPane();
    grid.getChildren().add(view);

    grid.setAlignment(Pos.TOP_CENTER);

    BorderPane border = new BorderPane();
    border.setCenter(grid);

    root.setScene(new Scene(border));
    root.setMaximized(true);
    root.show();
  }
}

结果: https://i.imgur.com/eWvBQy6.png

带有滚动窗格的代码:

public class ImageDB extends Application
{
  @Override
  public void start(Stage root)
  {
    Image image = new Image("0.jpg");
    ImageView view = new ImageView();
    view.setImage(image);

    ScrollPane scroll = new ScrollPane();
    scroll.setContent(view);

    GridPane grid = new GridPane();
    grid.getChildren().add(scroll);

    grid.setAlignment(Pos.TOP_CENTER);

    BorderPane border = new BorderPane();
    border.setCenter(scroll);

    root.setScene(new Scene(border));
    root.setMaximized(true);
    root.show();
  }
}

结果: https://i.imgur.com/1aJDX4m.png

发生的情况是,当网格窗格居中滚动窗格时,图像视图位于滚动窗格的左上角。

一个解决方法是将图像视图包裹在堆栈窗格中(默认居中),并确保堆栈窗格至少与滚动窗格的视口一样宽:

SSCCE:

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.HPos;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class ImageDB extends Application
{
  @Override
  public void start(Stage root)
  {
//    Image image = new Image("0.jpg");
    Image image = createImage();
    ImageView view = new ImageView();
    view.setImage(image);
    StackPane imageHolder = new StackPane(view);

    ScrollPane scroll = new ScrollPane();
    scroll.setContent(imageHolder);

    GridPane grid = new GridPane();

    imageHolder.minWidthProperty().bind(Bindings.createDoubleBinding(() -> 
        scroll.getViewportBounds().getWidth(), scroll.viewportBoundsProperty()));
    grid.getChildren().add(imageHolder);

//    grid.setAlignment(Pos.TOP_CENTER);

    BorderPane border = new BorderPane();
    border.setCenter(scroll);

    root.setScene(new Scene(border));
    root.setMaximized(true);
    root.show();
  }

  private Image createImage() {
      return new Rectangle(400, 200, Color.CORNFLOWERBLUE).snapshot(null, null);
  }

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

虽然 imageHolder.minWidthProperty().bind(Bindings.createDoubleBinding(() -> scroll.getViewportBounds().getWidth(), scroll.viewportBoundsProperty())); 确实有效,但它似乎存在一些问题(例如,有时会显示错误的 scrollbars/flickering 内容等)。

我后来发现一个简单的 scroll.setFitToWidth(true); (and/or scroll.setFitToHeight(true);) 也可以使内容与滚动窗格保持一致,但没有上述问题!

通过 viewportBoundsProperty 的更好解决方案:

import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class ImageDB extends Application {
    final static int WIDTH = 400;
    final static int HEIGHT = 200;

    @Override
    public void start(Stage root) {
        // Image image = new Image("0.jpg");
        Image image = this.createImage();
        ImageView view = new ImageView();
        view.setImage(image);
        GridPane grid = new GridPane();
        grid.getChildren().add(view);

        ScrollPane scroll = new ScrollPane();
        scroll.setContent(grid);

        BorderPane border = new BorderPane();
        border.setCenter(scroll);

        this.center(scroll.getViewportBounds(), grid);
        scroll.viewportBoundsProperty().addListener((observable, oldValue, newValue) -> {
            this.center(newValue, grid);
        });

        root.setScene(new Scene(border));
        root.setMaximized(true);
        root.show();
    }

    private Image createImage() {
        return new Rectangle(ImageDB.WIDTH, ImageDB.HEIGHT, Color.CORNFLOWERBLUE).snapshot(null, null);
    }

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

    private void center(Bounds viewPortBounds, Node centeredNode) {
        double width = viewPortBounds.getWidth();
        double height = viewPortBounds.getHeight();
        if (width > ImageDB.WIDTH) {
            centeredNode.setTranslateX((width - ImageDB.WIDTH) / 2);
        } else {
            centeredNode.setTranslateX(0);
        }
        if (height > ImageDB.HEIGHT) {
            centeredNode.setTranslateY((height - ImageDB.HEIGHT) / 2);
        } else {
            centeredNode.setTranslateY(0);
        }

    }

}

这在某些情况下可能会有所帮助:

<ScrollPane fitToWidth="true" fitToHeight="true">