简体   繁体   English

在 JavaFX 中绑定字体大小?

[英]Bind Font Size in JavaFX?

I'd like to make my application fluid.我想让我的应用程序流畅。 However, the fonts look small compared to the UI elements when I make the windows bigger.但是,当我将窗口放大时,与 UI 元素相比,字体看起来很小。 Ultimately, I want the size of my text to get bigger or smaller when I resize the window.最终,当我调整窗口大小时,我希望我的文本大小变大或变小。 I know I could theoretically do this with the style property, but I already have that property bound to something else in some instances.我知道理论上我可以使用 style 属性做到这一点,但在某些情况下我已经将该属性绑定到其他东西。

I know there's the "fontProperty" method, but I have nothing to bind it to since I can't figure out how to create a dynamic ObjectProperty with a synced size.我知道有“fontProperty”方法,但我没有任何东西可以绑定它,因为我不知道如何创建具有同步大小的动态 ObjectProperty。 What should I do?我应该怎么办?

EDIT: To avoid confusion, I'm trying to make the Font size change based on other factors, not the other way around.编辑:为避免混淆,我试图根据其他因素更改字体大小,而不是相反。

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.我不确定您的样式已经绑定到什么,但您可以在样式字符串中设置多个 css 值。

Set the .root -fx-font-size设置 .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:在 sylesheet .root选择器中,将-fx-font-size设置为您想要的值:

    .root { -fx-font-size: 40px; .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).所有默认控件都基于em单位(基于默认字体大小)。
  2. -fx-font-size is an inherited style. -fx-font-size继承的样式。
  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相关样本

Related Information相关信息

em is a generic unit that is not specific to JavaFX and em units are also used in HTML CSS. em是一个不特定于 JavaFX 的通用单位,并且 em 单位也用于 HTML CSS。 If interested, you can read a broader discussion on em units versus other units .如果有兴趣,您可以阅读有关 em 单位与其他单位的更广泛讨论

Using em units in FXML via expression binding通过表达式绑定在 FXML 中使用 em 单位

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.只需设置默认字体大小,您就可以到达所需位置的大约 90%,但不一定是通用修复,因为某些布局单位可能指定为不使用 em 单位。 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.大多数情况下,这并不是真正的问题,但如果是您的情况,您还可以应用Oracle 开发人员邮件列表帖子中描述的机制,这似乎可行,但有点笨拙。

How about using an expression binding.如何使用表达式绑定。

For 35em x 25em you could write:对于 35em x 25em,你可以写:

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

It's not 100% concise, but perhaps passable.它不是 100% 简洁,但也许还过得去。

These kind of sizing expressions work in scene builder 1.1, which is nice.这些大小表达式在场景构建器 1.1 中工作,这很好。


Here is an example using a Rectangle to store the width and height modifiers for the fxml file.这是一个使用 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>

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>

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

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

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

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.使用此解决方案,动态字体调整非常流畅和快速,甚至超过了bind(Bindings.concat("-fx-font-size: "方法。

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"在 CSS 和 FXML 文件中,我以相对方式定义了所有字体大小,仅使用“em”而不是“%”或“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 (eg 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.它之所以有效,是因为设置为主窗格的样式将作为修饰符影响已在 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.因此,您不必找到每个具有 Font 属性的节点即可使其正常工作。

Sample files:示例文件:

styles.css样式.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主控制器.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("%;"));
            }
        });
    }
}

I know this is a very old question but since 19-ea+9 you may use like我知道这是一个非常古老的问题,但是从 19-ea+9 开始,您可以使用 like

private DoubleProperty fontSize = new SimpleDoubleProperty(10);
node.styleProperty().bind(fontSize.map(size -> "-fx-font-size: "+size+";-fx-base: rgb(0,0,255);"));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM