【问题标题】:Bind Font Size in JavaFX?在 JavaFX 中绑定字体大小?
【发布时间】:2014-07-05 12:27:23
【问题描述】:

我想让我的应用程序流畅。但是,当我将窗口放大时,与 UI 元素相比,字体看起来很小。最终,当我调整窗口大小时,我希望我的文本大小变大或变小。我知道理论上我可以使用 style 属性做到这一点,但在某些情况下我已经将该属性绑定到其他东西。

我知道有“fontProperty”方法,但我没有任何东西可以绑定它,因为我不知道如何创建具有同步大小的动态 ObjectProperty。我该怎么办?

编辑:为避免混淆,我尝试根据其他因素更改字体大小,而不是相反。

【问题讨论】:

    标签: java user-interface data-binding fonts javafx


    【解决方案1】:

    只需将您希望字体大小更改的所有内容放入容器中,然后设置该样式或根据需要使用绑定。

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

    我不确定您的样式已经绑定到什么,但您可以在样式字符串中设置多个 css 值。

    【讨论】:

    • 那是我最初的想法,但是某些对象的-fx-styles 是动态绑定到各种其他属性的。没有具体的方法来检测它绑定的内容,所以我不能使用它。我怀疑 JavaFX 的人会创建 FontProperty,你可以用它做任何实质性的事情。
    • 没关系,只要把你想改变字体的所有东西都放在一个容器里。请注意 vbox 子项中的所有内容如何更改字体。您可以拥有具有不同 css 值的容器层。
    • 就像我说的,这是动态完成的。你是说我必须将我修改的每个组件都封装在它自己的容器中才能应用 CSS 吗?这似乎是一个非常丑陋的解决方法,但如果我只有这一切,我想它必须这样做:/。
    • 不,仅当您希望同一元素具有不同的 css 值时,例如具有不同的字体。正如您在我的示例中看到的,我使用 concat 将 2 个不相关的 css 值绑定到相同的样式字符串。您说您的样式属性已经绑定,只需将 concat 添加到您想要的任何其他内容。
    • 对我来说,这不会改变子标签的字体大小。他们可以解决这个问题吗?
    【解决方案2】:

    设置 .root -fx-font-size

    1. 为您的应用程序创建一个custom stylesheet
    2. 在样式表.root 选择器中,将-fx-font-size 设置为您想要的值:

      .root { -fx字体大小:40px; }

    为什么会这样

    之所以有效,是因为:

    1. 所有默认控件均基于em 单位(基于默认字体大小)。
    2. -fx-font-sizeinherited 样式。
    3. 一切都从根继承。

    执行此操作后,所有控件(以及其中的文本)都会自动调整大小以适应您指定的任何字体大小。

    相关示例

    相关信息

    em 是一个不特定于 JavaFX 的通用单位,并且 em 单位也用于 HTML CSS。有兴趣的可以阅读broader discussion on em units versus other units

    通过表达式绑定在 FXML 中使用 em 单位

    仅设置默认字体大小即可让您达到所需位置的大约 90%,但不一定是通用修复,因为某些布局单位可能未使用 em 单位指定。大多数情况下,这并不是一个真正的问题,但如果是您的情况,您也可以应用Oracle developer mailing list post 中描述的机制,这似乎可行,但有点笨拙。

    如何使用表达式绑定。

    对于 35em x 25em,您可以这样写:

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

    它不是 100% 简洁,但也许还过得去。

    这些大小表达式在场景构建器 1.1 中工作,这很好。


    这是一个使用 Rectangle 来存储 fxml 文件的宽度和高度修饰符的示例。

    <?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>
    

    或者,您可以创建自己的单元类并将其用于您的大小调整表达式,例如:

    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>
    

    【讨论】:

    • 我不想将绑定到字体大小,我想真正改变字体本身。这不是反其道而行之吗?无论哪种方式,我都会对此表示赞成,因为我将来肯定会使用它。我什至不知道这是可能的:)。
    • 请编辑您的问题以更详细地解释您要做什么。
    • 我多加了一句。现在我读了它,我可以看到你是如何解释这个问题的。对此感到抱歉。
    • 你是 JavaFX 之神,JewelSea。我已经用这个答案解决了一个完全不相关的问题。既然你在这个项目上帮助了我这么多,我会继续把你放在学分中。现在,如果我可以调整字体大小,它应该让每个人都可以在初始屏幕上清楚地看到你的名字:D。感谢您的所有帮助!
    【解决方案3】:

    有一种最简单且合适的方法将字体大小与容器大小绑定,以使其具有响应式字体缩放效果。

    bind(Bindings.concat("-fx-font-size: " 是一个不错的选择,但是当您调整窗口大小时,它似乎很慢,我认为这不是解决问题的正确方法。

    您可以声明 FontProperty 并将此 FontProperty 绑定到组件(标签、文本等),最后创建一个事件以根据我们的设计将大小与 FontProperty 大小绑定:

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

    然后在构造函数或一些 init 方法中,您可以将我们对象的字体属性与我们的动态字体绑定:

    textTracking.fontProperty().bind(fontTracking);
    

    最后,您需要将包装容器更改大小与动态字体大小绑定:

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

    使用此方案,动态字体调整非常流畅和快速,甚至超过bind(Bindings.concat("-fx-font-size: "方法。

    【讨论】:

    • ObjectExpression&lt;Font&gt; fontTracking = Bindings.createObjectBinding(() -&gt; Font.font(getWidth() / 4), widthProperty()) 然后textTracking.fontProperty().bind(fontTracking) 可能更容易。
    • 我认为这是一个比我更好的解决方案。 7 年后,我又获得了一次投票,然后来看看为什么。我知道我的方法较慢。
    【解决方案4】:

    我遇到了类似的问题,用这种方法解决了:

    1. 在 CSS 和 FXML 文件中,我以相对方式定义了所有字体大小,仅使用“em”而不是“%”或“px”
    2. 在主窗口的控制器中,我将主窗格的样式属性中的双精度值绑定到某个场景大小属性(例如,其对角线长度的粗略近似值)

    之所以有效,是因为设置到主窗格的样式将作为修饰符影响已在 FXML 中定义的所有节点。已有样式类的节点也会受到影响,因为新样式作为层叠样式表中的额外顶层样式出现。

    此解决方案的主要优点是在一个地方处理字体大小缩放系数。因此,您不必找到每个具有 Font 属性的节点即可使其正常工作。

    示例文件:

    styles.css

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

    ma​​in.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("%;"));
                }
            });
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-27
      • 1970-01-01
      • 2018-11-01
      • 2016-01-11
      相关资源
      最近更新 更多