Javafx:将字体大小绑定(bind)到容器

标签 javafx fonts binding size fxml

我目前正在做最后一年的项目,即创建一个数独游戏/求解器。我遇到的问题是,在使用场景框架时,我找不到将字体大小调整为容器字体大小的方法。

我希望将标签的字体大小更改为其所在网格 Pane 的相应大小。然而,我发现很难将我需要的属性(例如“widthProperty”和“heightProperty”)绑定(bind)到“Font.font(size)”的属性

这个问题与其他绑定(bind)问题的区别在于,我的场景框架会干扰将控件的绑定(bind)放入 Controller 的初始化方法中。我无法执行此操作,因为我的所有 Controller 都是在启动时初始化的,而不是在它们显示到场景时初始化的

这是我的所有代码:

Main.java

package Controller;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class Main extends Application {

    public static String mainMenu = "Main Menu";
    public static String mainMenuFXML = "/FXML/MainMenu.fxml";
    public static String chooseLevel = "Level Selection";
    public static String chooseLevelFXML = "/FXML/ChooseLevel.fxml";

    //Global Variable to be accessed across all classes
    //To be used to determine which scenes are loaded in the HashMap and also which scene has been set
    public static SceneFramework mainController = new SceneFramework();

    @Override
    public void start(Stage primaryStage) throws Exception{

        //Only the main menu scene is needed to be loaded at the start
        mainController.loadScene(Main.mainMenu, Main.mainMenuFXML);
        mainController.setScene(Main.mainMenu);

        StackPane root = new StackPane();
        root.getChildren().addAll(mainController);
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {

        launch(args);
    }
}

SceneFramework.java

package Controller;


import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.layout.StackPane;

import java.util.HashMap;

public class SceneFramework extends StackPane{

    //Constructor for the class
    public SceneFramework(){
        //The keyword super, overrides methods from the superclass
        super();
    }

    //This HashMap is used to hold the screens(scenes) to be used within the application
    protected HashMap<String, Node> scenes = new HashMap<>();

    //Adds the selected scene to the HashMap
    public void addScene(String title, Node scene) {
        scenes.put(title, scene);
    }

    //Returns the Node with the specified title for use within application
    public Node getScene(String title) {
        return scenes.get(title);
    }

    //Removes the selected scene from the framework
    public boolean unloadScene(String title){
        if(scenes.remove(title) == null){
            System.out.println("Scene cannot be located or it doesn't exist");
            return false;
        } else{
            return true;
        }
    }

    public boolean loadScene(String title, String resource){
        //encase code segment in try for exception handling,
        // as resources are required to be found and then loaded into memory
        try{
            //Load FXML File
            FXMLLoader loadedFXMLFile = new FXMLLoader(getClass().getResource(resource));

            //Get Parent of scene
            Parent loadScene = loadedFXMLFile.load();

            //Get the Controller class of the parent
            SetSceneParent sceneController = (loadedFXMLFile.getController());

            //Method of making sure every scene knows who it's parent is
            sceneController.setSceneParent(this);

            //Add scene to HashMap
            addScene(title, loadScene);
            return true;

        }catch (Exception e){
            //If FXML resource can't be loaded then generate this
            System.out.println("Could't load FXML file");
            return false;
        }
    }

    //Method for showing scenes
    //If one scenes if wanting to be shown then it is added to the root
    //If multiple scenes are wanting to be loaded then the first scene is removed and the new scene is then displayed
    public boolean setScene(final String title){
        //Check to see if scene can be found
        if(scenes.get(title) != null) {
            if(!getChildren().isEmpty()){

                getChildren().remove(0);
                getChildren().add(0 ,scenes.get(title));

            }else{
                    getChildren().add(scenes.get(title));
            }
            return true;
        } else{
            System.out.println("Scene not located!!!");
            return false;
        }
    }
}

SetSceneParent.java

package Controller;

import Controller.SceneFramework;

public interface SetSceneParent {

   void setSceneParent(SceneFramework sceneParent);
}

MainMenuController.java

package Controller;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;

import java.net.URL;
import java.util.ResourceBundle;


public class MainMenuController implements Initializable, SetSceneParent {

    @FXML
    public Button playButton, optionsButton, quitButton;


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        SelectionStatus.initialize();
    }

    @Override
    public void setSceneParent(SceneFramework sceneParent) {
        //Sets the parent of the scene by using the global variable of the class SceneFramework
        Main.mainController = sceneParent;
    }

    public void handlePlayButtonAction(){
        //On clicking of the play button
        //The user is taken to the level difficulty scene

        Main.mainController.loadScene(Main.chooseLevel, Main.chooseLevelFXML);
        Main.mainController.setScene(Main.chooseLevel);
        Main.mainController.unloadScene(Main.mainMenu);
    }

}

MainMenu.fxml

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

<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<BorderPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="480.0" prefWidth="640.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller.MainMenuController">
   <center>
      <HBox>
         <children>
            <VBox alignment="CENTER" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" BorderPane.alignment="TOP_CENTER" HBox.hgrow="ALWAYS">
                <children>
                  <Label alignment="TOP_CENTER" maxHeight="120.0" prefHeight="60.0" text="Sudoku Solver" textAlignment="CENTER" underline="true" wrapText="true" VBox.vgrow="ALWAYS">
                     <font>
                        <Font name="System Bold" size="48.0" />
                     </font>
                     <VBox.margin>
                        <Insets right="10.0" />
                     </VBox.margin>
                  </Label>
                    <Button fx:id="playButton" maxHeight="175.0" maxWidth="1400.0" mnemonicParsing="false" onAction="#handlePlayButtonAction" prefHeight="60.0" prefWidth="300.0" text="Play" VBox.vgrow="ALWAYS">
                        <VBox.margin>
                            <Insets />
                        </VBox.margin>
                     <font>
                        <Font name="System Bold" size="18.0" />
                     </font>
                    </Button>
                  <Region maxHeight="80.0" prefHeight="0.0" VBox.vgrow="ALWAYS" />
                    <Button fx:id="optionsButton" maxHeight="175.0" maxWidth="1400.0" mnemonicParsing="false" prefHeight="60.0" prefWidth="300.0" text="Options" VBox.vgrow="ALWAYS">
                        <VBox.margin>
                            <Insets />
                        </VBox.margin>
                     <font>
                        <Font name="System Bold" size="18.0" />
                     </font>
                    </Button>
                  <Region maxHeight="80.0" prefHeight="0.0" VBox.vgrow="ALWAYS" />
                    <Button fx:id="quitButton" maxHeight="175.0" maxWidth="1400.0" mnemonicParsing="false" prefHeight="60.0" prefWidth="300.0" text="Quit" VBox.vgrow="ALWAYS">
                        <VBox.margin>
                            <Insets />
                        </VBox.margin>
                     <font>
                        <Font name="System Bold" size="18.0" />
                     </font>
                    </Button>
                </children>
            </VBox>
         </children>
      </HBox>
   </center>
   <left>
      <HBox BorderPane.alignment="CENTER">
         <children>
            <Region maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefWidth="100.0" HBox.hgrow="ALWAYS" />
         </children>
      </HBox>
   </left>
   <right>
      <HBox BorderPane.alignment="CENTER">
         <children>
            <Region maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefWidth="100.0" HBox.hgrow="ALWAYS" />
         </children>
      </HBox>
   </right>
   <bottom>
      <VBox BorderPane.alignment="CENTER">
         <children>
            <Region prefHeight="60.0" VBox.vgrow="ALWAYS" />
         </children>
      </VBox>
   </bottom>
</BorderPane>

ChooseLevelController.java

package Controller;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;

import java.net.URL;
import java.util.ResourceBundle;

public class ChooseLevelController implements Initializable, SetSceneParent {

    @FXML
    public Label lblLevelSelection, lblLevel1, lblLevel2, lblLevel3, lblLevel4, lblLevel5, lblLevel6, lblLevel7, lblLevel8, lblLevel9, lblLevel10; // Value injected by FXMLLoader
    public Button backButton;
    public GridPane gridPaneCenter;

    @Override
    public void initialize(URL location, ResourceBundle resources) {

        //Assert code is to be used in debugging making sure references to files and their elements are correct
        assert lblLevel1 != null : "fx:id=\"lblLevel1\" was not injected: check your FXML file 'ChooseLevel.fxml'.";
        assert lblLevel2 != null : "fx:id=\"lblLevel2\" was not injected: check your FXML file 'ChooseLevel.fxml'.";
        assert lblLevel3 != null : "fx:id=\"lblLevel3\" was not injected: check your FXML file 'ChooseLevel.fxml'.";
        assert lblLevel4!= null : "fx:id=\"lblLevel4\" was not injected: check your FXML file 'ChooseLevel.fxml'.";
        assert lblLevel5 != null : "fx:id=\"lblLevel5\" was not injected: check your FXML file 'ChooseLevel.fxml'.";
        assert lblLevel6 != null : "fx:id=\"lblLevel6\" was not injected: check your FXML file 'ChooseLevel.fxml'.";
        assert lblLevel7 != null : "fx:id=\"lblLevel7\" was not injected: check your FXML file 'ChooseLevel.fxml'.";
        assert lblLevel8 != null : "fx:id=\"lblLevel8\" was not injected: check your FXML file 'ChooseLevel.fxml'.";
        assert lblLevel9 != null : "fx:id=\"lblLevel9\" was not injected: check your FXML file 'ChooseLevel.fxml'.";
        assert lblLevel10 != null : "fx:id=\"lblLevel10\" was not injected: check your FXML file 'ChooseLevel.fxml'.";

    }

    @Override
    public void setSceneParent(SceneFramework screenParent) {
        //Sets the parent of the scene by using the global variable of the class SceneFramework
        Main.mainController = screenParent;
    }

    //Logic code
    @FXML
    public void mouseClickedLevelLabel() {

        //Load the play screen when any Label is pressed
        lblLevel1.setOnMouseClicked(e -> handleLabelClick(1));
        lblLevel2.setOnMouseClicked(e -> handleLabelClick(2));
        lblLevel3.setOnMouseClicked(e -> handleLabelClick(3));
        lblLevel4.setOnMouseClicked(e -> handleLabelClick(4));
        lblLevel5.setOnMouseClicked(e -> handleLabelClick(5));
        lblLevel6.setOnMouseClicked(e -> handleLabelClick(6));
        lblLevel7.setOnMouseClicked(e -> handleLabelClick(7));
        lblLevel8.setOnMouseClicked(e -> handleLabelClick(8));
        lblLevel9.setOnMouseClicked(e -> handleLabelClick(9));
        lblLevel10.setOnMouseClicked(e -> handleLabelClick(10));
    }

    @FXML
    public void handleBackButtonAction() {
        //On clicking of the back button
        //The user is taken to the level difficulty scene

        Main.mainController.loadScene(Main.mainMenu, Main.mainMenuFXML);
        Main.mainController.setScene(Main.mainMenu);
        Main.mainController.unloadScene(Main.chooseLevel);

    }
}

选择Level.fxml

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

<?import javafx.geometry.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<BorderPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller.ChooseLevelController">
   <center>
      <VBox alignment="CENTER" BorderPane.alignment="CENTER">
         <children>
            <GridPane fx:id="gridPaneCenter" alignment="CENTER" maxHeight="1.7976931348623157E308" maxWidth="1500.0" prefHeight="242.0" prefWidth="475.0" BorderPane.alignment="CENTER" VBox.vgrow="ALWAYS">
              <columnConstraints>
                <ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
                <ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
                  <ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
                  <ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
                  <ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
              </columnConstraints>
              <rowConstraints>
                <RowConstraints maxHeight="1.7976931348623157E308" minHeight="10.0" prefHeight="137.0" vgrow="ALWAYS" />
                <RowConstraints maxHeight="1.7976931348623157E308" minHeight="10.0" prefHeight="138.0" vgrow="ALWAYS" />
              </rowConstraints>
               <children>
                  <Label fx:id="lblLevel1" onMousePressed="#mouseClickedLevelLabel" text="1" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.vgrow="ALWAYS">
                     <font>
                        <Font name="System Bold" size="36.0" />
                     </font>
                  </Label>
                  <Label fx:id="lblLevel2" onMouseClicked="#mouseClickedLevelLabel" onMousePressed="#mouseClickedLevelLabel" text="2" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.vgrow="ALWAYS">
                     <font>
                        <Font name="System Bold" size="36.0" />
                     </font>
                  </Label>
                  <Label fx:id="lblLevel3" onMousePressed="#mouseClickedLevelLabel" text="3" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.vgrow="ALWAYS">
                     <font>
                        <Font name="System Bold" size="36.0" />
                     </font>
                  </Label>
                  <Label fx:id="lblLevel4" onMousePressed="#mouseClickedLevelLabel" text="4" GridPane.columnIndex="3" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.vgrow="ALWAYS">
                     <font>
                        <Font name="System Bold" size="36.0" />
                     </font>
                  </Label>
                  <Label fx:id="lblLevel5" onMousePressed="#mouseClickedLevelLabel" text="5" GridPane.columnIndex="4" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.vgrow="ALWAYS">
                     <font>
                        <Font name="System Bold" size="36.0" />
                     </font>
                  </Label>
                  <Label fx:id="lblLevel6" onMouseClicked="#mouseClickedLevelLabel" onMousePressed="#mouseClickedLevelLabel" text="6" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.rowIndex="1" GridPane.vgrow="ALWAYS">
                     <font>
                        <Font name="System Bold" size="36.0" />
                     </font>
                  </Label>
                  <Label fx:id="lblLevel7" onMousePressed="#mouseClickedLevelLabel" text="7" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.rowIndex="1" GridPane.vgrow="ALWAYS">
                     <font>
                        <Font name="System Bold" size="36.0" />
                     </font>
                  </Label>
                  <Label fx:id="lblLevel8" onMousePressed="#mouseClickedLevelLabel" text="8" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.rowIndex="1" GridPane.vgrow="ALWAYS">
                     <font>
                        <Font name="System Bold" size="36.0" />
                     </font>
                  </Label>
                  <Label fx:id="lblLevel9" onMousePressed="#mouseClickedLevelLabel" text="9" GridPane.columnIndex="3" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.rowIndex="1" GridPane.vgrow="ALWAYS">
                     <font>
                        <Font name="System Bold" size="36.0" />
                     </font>
                  </Label>
                  <Label fx:id="lblLevel10" onMousePressed="#mouseClickedLevelLabel" text="10" GridPane.columnIndex="4" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.rowIndex="1" GridPane.vgrow="ALWAYS">
                     <font>
                        <Font name="System Bold" size="36.0" />
                     </font>
                  </Label>
               </children>
            </GridPane>
         </children>
      </VBox>
   </center>
   <top>
      <GridPane fx:id="gridPaneTop">
         <columnConstraints>
            <ColumnConstraints hgrow="ALWAYS" maxWidth="207.0" minWidth="10.0" prefWidth="20.0" />
            <ColumnConstraints hgrow="SOMETIMES" maxWidth="207.0" minWidth="10.0" prefWidth="58.0" />
            <ColumnConstraints hgrow="ALWAYS" maxWidth="207.0" minWidth="10.0" prefWidth="105.0" />
            <ColumnConstraints hgrow="ALWAYS" maxWidth="451.0" minWidth="10.0" prefWidth="418.0" />
         </columnConstraints>
         <rowConstraints>
            <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
         </rowConstraints>
         <children>
            <Button fx:id="backButton" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#handleBackButtonAction" prefHeight="30.0" prefWidth="0.0" text="Back" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.valignment="CENTER" />
            <Separator prefHeight="63.0" prefWidth="73.0" visible="false" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.valignment="CENTER" GridPane.vgrow="ALWAYS" />
            <Label fx:id="lblLevelSelection" text="Level Selection" underline="true" GridPane.columnIndex="3">
               <font>
                  <Font name="System Bold" size="36.0" />
               </font>
            </Label>
            <Separator prefHeight="63.0" prefWidth="73.0" visible="false" />
         </children>
      </GridPane>
   </top>
   <bottom>
      <Region prefHeight="36.0" prefWidth="600.0" BorderPane.alignment="CENTER" />
   </bottom>
   <left>
      <HBox>
         <children>
            <Region maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="50.0" BorderPane.alignment="CENTER" HBox.hgrow="ALWAYS" />
         </children>
      </HBox>
   </left>
   <right>
      <HBox>
         <children>
            <Region maxWidth="1.7976931348623157E308" prefHeight="300.0" prefWidth="50.0" BorderPane.alignment="CENTER" HBox.hgrow="ALWAYS" />
         </children>
      </HBox>
   </right>
</BorderPane>

最佳答案

好吧,你的代码不会再次编译。所以我只发布一个最小的例子:

App.java

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class App extends Application {
    @Override
    public void start(Stage primaryStage) {
        View view = new View();
        Scene scene = new Scene(view, 400, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }  
    public static void main(String[] args) {
        launch(args);
    } 
}

View.java

import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.control.Label;
import javafx.scene.layout.*;

public class View extends AnchorPane {
    public View() {
        GridPane gridPane = new GridPane();
        ColumnConstraints column = new ColumnConstraints();
        column.setPercentWidth(33.33);
        column.setHalignment(HPos.CENTER);
        gridPane.getColumnConstraints().addAll(column, column, column);
        RowConstraints row = new RowConstraints();
        row.setPercentHeight(33.33);
        row.setValignment(VPos.CENTER);
        gridPane.getRowConstraints().addAll(row, row, row);
        gridPane.setGridLinesVisible(true);

        AnchorPane.setTopAnchor(gridPane, 0.0);
        AnchorPane.setBottomAnchor(gridPane, 0.0);
        AnchorPane.setLeftAnchor(gridPane, 0.0);
        AnchorPane.setRightAnchor(gridPane, 0.0);

        for ( int i = 0 ; i < 9; i++ ) {
            gridPane.add(new Label(i+1+""), i%3, i/3, 1, 1);
        }

        this.widthProperty().addListener( event -> {
            this.setStyle("-fx-font-size: " + this.getWidth()/10);
        });

        this.getChildren().add(gridPane);
    }
}

这正是我在您的其他问题上提出的建议......


编辑:

要更改确切元素的字体,您可以这样做:

    this.widthProperty().addListener( event -> {
        ObservableList<Node> labelList = gridPane.getChildren();
        for ( int i = 0; i < labelList.size(); i++ ) {
            //labelList.get(i).setStyle("-fx-font-size: " + this.getWidth()/10);
            if ( labelList.get(i).getClass().equals(Label.class) ) {
                Label.class.cast(labelList.get(i)).setFont(Font.font(this.getWidth()/10));
            }
        }
    });

编辑2:

要使用高度和宽度进行字体缩放,您可以使用如下方法:

private void changeFontSize(List<Node> labelList) {
    Double newFontSizeDouble = Math.hypot(this.getWidth(), this.getHeight())/10;
    int newFontSizeInt = newFontSizeDouble.intValue();

    for ( int i = 0; i < labelList.size(); i++ ) {
        if ( labelList.get(i).getClass().equals(Label.class) ) {
            Label.class.cast(labelList.get(i)).setFont(Font.font(newFontSizeInt));
        }
    }
}

并这样调用它:

this.widthProperty().addListener( event -> changeFontSize(gridPane.getChildren()));
this.heightProperty().addListener( event -> changeFontSize(gridPane.getChildren()));

编辑3:

您还可以使用属性绑定(bind)来绑定(bind)它们。 YouTube 上有一个很好的教程:

https://www.youtube.com/watch?v=s8GomyEOA8w&index=29&list=PL6gx4Cwl9DGBzfXLWLSYVy8EbTdpGbUIG

关于Javafx:将字体大小绑定(bind)到容器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35392002/

相关文章:

c++ - 绑定(bind)到临时。 l/r 值

c# - 将新项目添加到列表时,ListView ItemSsource 和 RaisePropertyChanged 不起作用

android - 如何通过 Activity 绑定(bind)从抽屉标题布局中获取 View ?

java - 为什么显示 "java.lang.reflect.InvocationTargetException"

javafx - 如何使用三个或更多键创建 JavaFX KeyCombination?

ios - 从 UIFont 或 .ttf 文件中获取字体的每个字符(带形状)

flash - 在 ActionScript 中读取和绘制像素字体

java - 插入 blob 作为数据类型时出现数据截断错误

java - 组合框 CSS 代码

css - font-face 在 IE 中运行但在 firefox 中不运行 - 字体格式