猛禽洛的程式筆記庫

[JavaFx] Fxml中畫面等比縮放的設計方式

在開發視窗軟體時幾乎都會遇到一個問題:放大縮小跑版

要因應各種不同的解析度/螢幕比例,最好的方式就是根據不同螢幕去做最佳化,但是這是一種幾乎做不完的工程,於是我在開發程式時的思路就是將UI都比照影片的方式辦理,進行全畫面的等比放大/縮小,既能節省開發時間,也能減少跑版問題。

1.Fxml檔的root元件依照以下方式排列與設定屬性:

root使用GridPane,並且裡面放一個AnchorPane比較好設計畫面。

GridPane依照以下屬性設定:

然後AnchorPane也設定成以下屬性:

說明:

1.GridPane的背景顏色為APP的底色

2.GridPane跟AnchorPane的Pref Widtth跟Pref Height
需要一個基本解析度做開發,如果螢幕小於這個解析度,還是會遇到顯示問題(畫面超出螢幕範圍),所以這邊須配合軟體系統需求做規劃。
這兩個元件的解析度需要設成一樣,才能同步縮放。

 

2.程式碼加入相對應處理:

由程式碼去監控畫面尺寸變化,並做比例縮放的動作

private MainActivityController mMainActivityController;

@Override
public void start(Stage stage) throws IOException {
  FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("fxml/activity_main.fxml"));
  Pane root = fxmlLoader.load();

  stage.setTitle("World");
  Scene scene = new Scene(root);
  stage.setScene(scene);
  stage.setMinWidth(800);
  stage.setMinHeight(600);
  stage.show();

  mMainActivityController = fxmlLoader.getController();

  //畫面縮放功能
  letterbox(scene, root);
}

private void letterbox(final Scene scene, final Pane contentPane) {
  final double initWidth  = scene.getWidth();
  final double initHeight = scene.getHeight();
  final double ratio      = initWidth / initHeight;

  SceneSizeChangeListener sizeListener = new SceneSizeChangeListener(scene, ratio, initHeight, initWidth, contentPane);
  scene.widthProperty().addListener(sizeListener);
  scene.heightProperty().addListener(sizeListener);
}

private record SceneSizeChangeListener(Scene scene, double ratio, double initHeight, double initWidth,
                     Pane contentPane) implements ChangeListener<Number> {

  @Override
  public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, Number newValue) {
    final double newWidth = scene.getWidth();
    final double newHeight = scene.getHeight();

    double scaleFactor =
        newWidth / newHeight > ratio
            ? newHeight / initHeight
            : newWidth / initWidth;

    //如須能縮小,則設為0(或一個最小的倍數)
    if (scaleFactor >= 1) {
      Scale scale = new Scale(scaleFactor, scaleFactor);
      //如需要中心放大,則使用(newWidth/2),(newHeight/2),測試用的root元件為GridPane,Alignment設為TOP_LEFT,第二層為AnchorPane,V/Halignment設為CENTER
      scale.setPivotX(newWidth / 2);
      scale.setPivotY(newHeight / 2);
      scene.getRoot().getTransforms().setAll(scale);

      contentPane.setPrefWidth(newWidth / scaleFactor);
      contentPane.setPrefHeight(newHeight / scaleFactor);
    } else {
      contentPane.setPrefWidth(Math.max(initWidth, newWidth));
      contentPane.setPrefHeight(Math.max(initHeight, newHeight));
    }
  }
}

 

這樣就能自由等比放大~全螢幕了

-END-

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *