猛禽洛的程式筆記庫

[JavaFx] 讓應用程式的畫面可以等比放大

我們在開發桌面程式時,對於視窗的放大縮小一直都不好控制,畫面一旦複雜起來,在縮放的過程中常常容易跑版。

於是就想說,如果可以像遊戲一樣,對整個畫面進行等比放大,那就會方便許多。

以下方法就是類似於遊戲整體放大方法,我們先以一個預設解析度來設計UI,最後讓者個畫面有等比縮放功能。

 

1.建立畫面架構

我們需要一個以GridPane為根的底,然後塞入一層AnchorPane。

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.*?>

<GridPane fx:id="gp_test" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="768.0" prefWidth="1366.0" stylesheets="@../css/style.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.lugia.controllers.TestActivityController">
   <columnConstraints>
      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
   </columnConstraints>
   <rowConstraints>
      <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
   </rowConstraints>
   <children>
      <AnchorPane fx:id="ap_test" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="768.0" prefWidth="1366.0" GridPane.halignment="CENTER" GridPane.valignment="CENTER" />
   </children>
</GridPane>

 

在GridPane中,主要需要設定好寬高,以pref參數為主,其他min與max的寬高度都設為USE_PREF_SIZE

在AnchorPane中,要設定valignment與halignment為CENTER,如上面程式碼。

 

接下來就可以在AnchorPane中放入自己的元件了。

 

2.畫面的main程式設定

package com.lugia;

import com.lugia.controllers.MainActivityController;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.transform.Scale;
import javafx.stage.Stage;

public class MainActivity extends Application {

    private MainActivityController controller;

    @Override
    public void start(Stage primaryStage) throws Exception{
        //反鋸齒
        System.setProperty("prism.lcdtext", "false");

    FXMLLoader fxmlLoader = new FXMLLoader(getClass().getClassLoader().getResource("fxml/main_activity.fxml"));
    Pane root = fxmlLoader.load();
    primaryStage.setTitle("Test");

        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
        //視窗顯示確定大小後,再做最小尺寸限制
        primaryStage.setMinWidth(primaryStage.getWidth());
        primaryStage.setMinHeight(primaryStage.getHeight());

    controller = fxmlLoader.getController();

        letterbox(scene, root);
//        primaryStage.setFullScreen(true);//以全螢幕執行
    }

    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 static class SceneSizeChangeListener implements ChangeListener<Number> {
        private final Scene scene;
        private final double ratio;
        private final double initHeight;
        private final double initWidth;
        private final Pane contentPane;

        public SceneSizeChangeListener(Scene scene, double ratio, double initHeight, double initWidth, Pane contentPane) {
            this.scene = scene;
            this.ratio = ratio;
            this.initHeight = initHeight;
            this.initWidth = initWidth;
            this.contentPane = contentPane;
        }

        @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));
            }
        }
    }

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

    @Override
    public void stop() {
        controller.onClose();
    }
}

 

3.執行效果

原始大小(約1366*768):

畫面放到最大(約1920*1080):注意頂部TITLE跟畫面UI的比例,視窗整個放大後不會跑版

 

-END-

發佈留言

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