Reputation: 123
I am currently working on my final year project which is to create a Sudoku game/solver. The problem I have is that I am cannot find a method of resizing font size to that of its container when using a scene framework.
I would like to have the font size of a label change to the corresponding size of the gridpane it is within. I however am finding it hard to bind the properties I need such as 'widthProperty' and 'heightProperty' to that of 'Font.font(size)'
The difference of this question to other binding questions, is that my scene framework interferes with putting the binding for controls in the initialize method of a controller. I cannot do this as all of my controllers are initialized on start up not when they are shown to the scene
Here is all my code:
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);
}
}
ChooseLevel.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>
Upvotes: 2
Views: 3086
Reputation: 778
Ok your code does not compile AGAIN. So I'll just post a minimal example:
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 is EXACTLY what i proposed on your other question...
Edit:
To change the font of the exact element you could go about it like this:
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));
}
}
});
Edit2:
To have the font scale with the height AND the width you could use a method such as this:
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));
}
}
}
And call it like this:
this.widthProperty().addListener( event -> changeFontSize(gridPane.getChildren()));
this.heightProperty().addListener( event -> changeFontSize(gridPane.getChildren()));
Edit3:
You can also bind them using property bindings. There's a good tutorial on youtube here:
https://www.youtube.com/watch?v=s8GomyEOA8w&index=29&list=PL6gx4Cwl9DGBzfXLWLSYVy8EbTdpGbUIG
Upvotes: 2