Bind Font Size in JavaFX?

前端 未结 4 438
无人及你
无人及你 2020-11-27 06:54

I\'d like to make my application fluid. However, the fonts look small compared to the UI elements when I make the windows bigger. Ultimately, I want the size of my text to g

相关标签:
4条回答
  • 2020-11-27 07:17

    There is a simplest and proper method to bind font size with the container size, in order to make it in a responsive font scale effect.

    The bind(Bindings.concat("-fx-font-size: " is a good alternative but when you resize the windows it seems to be very slow and I think is not the proper way to resolve the problem.

    You can declare FontProperty and bind this FontProperty to the Component (Label, Text, etc.) and finally create an event to bind the size with the FontProperty size according with our design:

    private Label textTracking = new Label();
    private ObjectProperty<Font> fontTracking = new SimpleObjectProperty<Font>(Font.getDefault());
    

    Then in constructor or some init method you can bind the font property of our object with our dynamic font:

    textTracking.fontProperty().bind(fontTracking);
    

    Finally, you need to bind the wrapper container change size with the dynamic font size:

        widthProperty().addListener(new ChangeListener<Number>()
        {
            @Override
            public void changed(ObservableValue<? extends Number> observableValue, Number oldWidth, Number newWidth)
            {
                fontTracking.set(Font.font(newWidth.doubleValue() / 4));
            }
        });
    

    With this solution the dynamic font adjustment is very flow and fast, even more than bind(Bindings.concat("-fx-font-size: " method.

    0 讨论(0)
  • 2020-11-27 07:18

    I had a similar problem and solved it this way:

    1. in the CSS and FXML files, I defined all font sizes in a relative way, using only "em" instead of "%" or "px"
    2. in the controller of the main window, I bound a double value within a style property of the main pane to some scene size property (e.g. a rough approximation of its diagonal length)

    It works because the style that is set to the main pane will affect as a modifier all the nodes that are already defined in the FXML. The nodes that already have a style class are also impacted as the new style comes as an additional top layer of styling in the cascading style sheets.

    The main advantage of this solution is to handle in a single place the font size zoom factor. Thus you don't have to find every node that has a Font property to make it working.

    Sample files:

    styles.css

    .label {
        -fx-font-size: 1.1em;
    }
    
    .title {
        -fx-font-size: 1.5em;
        -fx-font-weight: bold;
    }
    

    main.fxml

    <?xml version="1.0" encoding="UTF-8"?>
    <?import javafx.scene.control.*?>
    <?import javafx.scene.control.cell.*?>
    <?import javafx.scene.layout.*?>
    <BorderPane style="-fx-background-color: #fff;"
            xmlns="http://javafx.com/javafx/8"
            xmlns:fx="http://javafx.com/fxml/1"
            prefWidth="1280.0" prefHeight="720.0"
            fx:controller="controllers.MainController">
        <top>
            <Label text="Application title" styleClass="title"/>
        </top>
        <center>
            <Label text="Lorem ipsum"/>
        </center>
    </BorderPane>
    

    MainController.java

    package controllers;
    
    import javafx.fxml.FXML;
    import javafx.fxml.Initializable;
    import javafx.scene.layout.BorderPane;
    
    public class MainController implements Initializable {
        @FXML private BorderPane mainPane;
    
        @Override
        public void initialize(URL url, ResourceBundle resourceBundle) {
            initializeFontSizeManager();
        }
    
        private void initializeFontSizeManager() {
            // Cf. https://stackoverflow.com/questions/13246211/javafx-how-to-get-stage-from-controller-during-initialization
            mainPane.sceneProperty().addListener((observableScene, oldScene, newScene) -> {
                // We need a scene to work on
                if (oldScene == null && newScene != null) {
                    DoubleProperty fontSize = new SimpleDoubleProperty(0);
                    fontSize.bind(newScene.widthProperty().add(newScene.heightProperty())
                        .divide(1280 + 720) // I know, it's a very rough approximation :)
                        .multiply(100)); // get a suitable value to put before the '%' symbol in the style
                    mainPane.styleProperty().bind(
                        Bindings.concat("-fx-font-size: ", fontSize.asString("%.0f")).concat("%;"));
                }
            });
        }
    }
    
    0 讨论(0)
  • 2020-11-27 07:22

    Set the .root -fx-font-size

    1. Create a custom stylesheet for your application.
    2. In sylesheet .root selector, set -fx-font-size to your desired value:

      .root { -fx-font-size: 40px; }

    Why this works

    This works because:

    1. All of the default controls are based on em units (which are based on the default font size).
    2. -fx-font-size is an inherited style.
    3. Everything inherits from the root.

    Once you do this, all controls (and the text inside them) will automatically resize nicely to fit whatever font size you specified.

    Related Sample

    • javafx automatic resizing and button padding

    Related Information

    em is a generic unit that is not specific to JavaFX and em units are also used in HTML CSS. If interested, you can read a broader discussion on em units versus other units.

    Using em units in FXML via expression binding

    Just setting a default font size gets you about 90% of the way to where you need to be, but is not necessarily a universal fix as some layout units might be specified not using em units. Most of the time this isn't really an issue, but if it is in your case, you could also apply a mechanism described in an Oracle developer mailing list post, which appears to work, though is a little clunky.

    How about using an expression binding.

    For 35em x 25em you could write:

    prefWidth="${35*u.em}" prefHeight="${25*u.em}"
    

    It's not 100% concise, but perhaps passable.

    These kind of sizing expressions work in scene builder 1.1, which is nice.


    Here is an example using a Rectangle to store the width and height modifiers for the fxml file.

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.control.*?>
    <?import javafx.scene.layout.*?>
    <?import javafx.scene.shape.*?>
    
    <StackPane xmlns:fx="http://javafx.com/fxml">
    <fx:define>
      <Rectangle fx:id="s" width="13" height="13"/>
    </fx:define>
    <AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="${22 * s.width}" prefWidth="${14 * s.height}">
      <children>
        <Button layoutX="${4 * s.width}" layoutY="${ 5 * s.height}" prefWidth="${6 * s.width}" text="Top" />
        <Button layoutX="${4 * s.width}" layoutY="${10 * s.height}" prefWidth="${6 * s.width}" text="Middle" />
        <Button layoutX="${4 * s.width}" layoutY="${15 * s.height}" prefWidth="${6 * s.width}" text="Bottom" />
      </children>
    </AnchorPane>
    </StackPane>
    

    Or instead, you can create your own unit class and use it in your sizing expressions, for example:

    package org.jewelsea.measure;
    
    public class Measurement {
      private double em;
      public  void   setEm(double em) { this.em = em; }
      public  double getEm()          { return em; }
    }
    
    . . .
    
    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.control.*?>
    <?import javafx.scene.layout.*?>
    <?import org.jewelsea.measure.Measurement?>
    <?scenebuilder-classpath-element .?>
    
    <StackPane xmlns:fx="http://javafx.com/fxml">
      <fx:define>
        <Measurement fx:id="u" em="26.0" />
      </fx:define>
      <AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="${22*u.em}" prefWidth="${14*u.em}">
        <children>
          <Button layoutX="${4*u.em}" layoutY="${ 5*u.em}" prefWidth="${6*u.em}" text="Top" />
          <Button layoutX="${4*u.em}" layoutY="${10*u.em}" prefWidth="${6*u.em}" text="Middle" />
          <Button layoutX="${4*u.em}" layoutY="${15*u.em}" prefWidth="${6*u.em}" text="Bottom" />
        </children>
      </AnchorPane>
    </StackPane>
    
    0 讨论(0)
  • 2020-11-27 07:26

    Just put everything where you want the fontsize to change in a container and set that style or use bindings if you want.

    import javafx.application.Application;
    import javafx.beans.binding.Bindings;
    import javafx.beans.property.DoubleProperty;
    import javafx.beans.property.IntegerProperty;
    import javafx.beans.property.SimpleDoubleProperty;
    import javafx.beans.property.SimpleIntegerProperty;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.Label;
    import javafx.scene.control.TextArea;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    public class FontBind extends Application {
    
        private DoubleProperty fontSize = new SimpleDoubleProperty(10);
        private IntegerProperty blues = new SimpleIntegerProperty(50);
    
        @Override
        public void start(Stage primaryStage) {
            Button btn = new Button("click me, I change color");
            btn.setOnAction((evt)->{blues.set(blues.get()+20);});//max?
            Label lbl = new Label("I'm a label");
            TextArea ta = new TextArea("Lots of text can be typed\nand even number 1234567890");
            HBox hbox = new HBox(new Label("I never change"));
            VBox child = new VBox(btn, lbl, ta);
            VBox root = new VBox(child, hbox);
            Scene scene = new Scene(root, 300, 250);
    
            fontSize.bind(scene.widthProperty().add(scene.heightProperty()).divide(50));
            child.styleProperty().bind(Bindings.concat("-fx-font-size: ", fontSize.asString(), ";"
                                                      ,"-fx-base: rgb(100,100,",blues.asString(),");"));
    
            primaryStage.setTitle("Hello World!");
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
    }
    

    I'm not sure what your style is already bound to, but you're allowed to set multiple css values in the style string.

    0 讨论(0)
提交回复
热议问题