放大或缩小文本以适合标签容器 JavaFX

Scale up or down text to fit label container JavaFX

基本上我想要实现的是希望字体大小自动调整。我有一个 window 是 1920*1080,标签是全宽和全高。我启用了文本换行,因为我计划在该软件中使用 运行 歌词。所以我们的想法是让文本适合容器尽可能大的字体。或者如果文本太多,不要消失到屏幕底部,只需将字体变小即可。我使用场景生成器制作了大部分框架,它运行 FXML。提前谢谢你的帮助。也许我所拥有的图像会有所帮助。[这是图像][1]

Presentation.fxml

<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="main.presentationController">
  <children>
     <Canvas fx:id="screen" height="1080.0" nodeOrientation="INHERIT" width="1920.0">
        <cursor>
           <Cursor fx:constant="NONE" />
        </cursor>
     </Canvas>
     <Label fx:id="displayText" alignment="CENTER" layoutX="-2.0" layoutY="-4.0" prefHeight="1085.0" prefWidth="1924.0" text="Стучатся молитвы в небо, Касаясь святых небес! Звучат в них печаль и горе, И боль, как глубокий порез! Молитвы такие о детях, Родители в небо кричат! Они днем и  ночью взывают! Не могут сердца их молчать!" textAlignment="CENTER" textFill="WHITE" textOverrun="WORD_ELLIPSIS" wrapText="true">
        <font>
           <Font size="79.0" />
        </font>
        <padding>
           <Insets bottom="40.0" left="40.0" right="40.0" top="40.0" />
        </padding>
        <cursor>
           <Cursor fx:constant="NONE" />
        </cursor>
     </Label>
  </children>
  <cursor>
     <Cursor fx:constant="NONE" />
  </cursor>

演示文稿Controller.java

    package main;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.Label;

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

public class presentationController implements Initializable {

    @FXML public Canvas screen;
    @FXML public Label displayText;
    public drawScreen toscreen;

    public presentationController() {
        toscreen = new drawScreen();

    }

    public void initialize(URL url, ResourceBundle resourceBundle) {
        toscreen.initd(screen,displayText);
        toscreen.draw();
        toscreen.displayText("Стучатся молитвы в небо, Касаясь святых небес!\n" +
                "Звучат в них печаль и горе,\n" +
                "И боль, как глубокий порез!\n" +
                "Молитвы такие о детях,\n" +
                "Родители в небо кричат!\n" +
                "Они днем и  ночью взывают!\n" +
                "Звучат в них печаль и горе,\n" +
                "И боль, как глубокий порез!\n" +
                "Молитвы такие о детях,\n" +
                "Родители в небо кричат!\n" +
                "Они днем и  ночью взывают!\n" +
                "Звучат в них печаль и горе,\n" +
                "И боль, как глубокий порез!\n" +
                "Молитвы такие о детях,\n" +
                "Родители в небо кричат!\n" +
                "Они днем и  ночью взывают!\n" +
                "Не могут сердца их молчать");
        
    }


}

绘制屏幕

    package main;

import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.image.Image;

import java.io.IOException;

public class drawScreen {
    Canvas previewScreen;
    Canvas displayScreen;
    public GraphicsContext g1;
    public GraphicsContext g2;
    public Label songLyricsPre;
    public Label songLyricsDis;
    public drawScreen(){

    }
    public void initp(Canvas previewScreen, Label text){
        this.previewScreen = previewScreen;
        this.songLyricsPre = text;
        System.out.println("Display Initialized: Preview");
    }
    public void initd( Canvas displayScreen, Label text){
        this.displayScreen = displayScreen;
        this.songLyricsDis = text;
        System.out.println("Display Initialized: Main");
    }
    public void draw(){
        Image i = new Image("/img/basic.png");

        try {
            if (previewScreen != null) {
                g1 = previewScreen.getGraphicsContext2D();
                g1.drawImage(i, 0, 0, previewScreen.getWidth(), previewScreen.getHeight());
            }
            if (displayScreen != null) {
                g2 = displayScreen.getGraphicsContext2D();
                g2.drawImage(i, 0, 0, displayScreen.getWidth(), displayScreen.getHeight());
            }
        } catch (Exception e){
            System.out.println("Failed to read Canvas");
        }
    }
    public void displayText(String text){
        try {
            if (previewScreen != null) {
                songLyricsPre.setText(text);
            }
            if (displayScreen != null) {
                songLyricsDis.setText(text);
            }
        } catch (Exception e){
            System.out.println("Failed to read Canvas");
        }

    }



}

我希望这足以帮助您了解发生了什么 [1]: https://i.stack.imgur.com/QKrLb.png

我的回答背后的基本思想是,比较 Label 及其容器的 widthheight 属性反映了显示的文本量(以及显示的文本量)已隐藏)。
当标签的字体加倍时,Labelwidthheight 加倍。

如果文本完美环绕且每行末尾没有多余空格,则标签的面积会增加四倍。我假设布局节点保持不变,标签的宽度也保持不变。任何额外的文本都会导致高度增加(并且文本会超出布局节点的边界并超出视图)。

目标是保持 label 和容器的面积相同(因此不会隐藏任何内容)。它们的面积之比就是它们的高度之比,因为宽度是恒定的。现在,字体应乘以的系数是面积比的平方根。 (因为面积与字体 ^2 成比例)。每次 Label 的高度改变时,字体的改变都可以按照上面的解释进行相应的改变。

由于每行末尾的额外空格,现在得出此方法的固有错误。每次更改字体时,额外空格的数量可能会增加或减少,导致计算后 Label 和容器的区域不匹配。重新计算往往会减少这个错误。在我的代码中,如果计算出的 font 与之前的误差在 5% 以内,则不会进行重新计算。

这是一个有效的应用程序:

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage stage) throws Exception{
        FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
        Parent root = loader.load();
        Controller c = loader.getController();
        stage.setTitle("Hello World");
        stage.setScene(new Scene(root));
        stage.show();
    }


    public static void main(String[] args) {
        launch(args);
    }
}
package sample;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;

public class Controller {
    @FXML Label label;
    double font;
    int error = 5;//%
    @FXML Pane pane;
    @FXML TextField input;

    @FXML void initialize(){
        font = 15;
        label.setStyle("-fx-font-size: " + font);

        label.heightProperty().addListener(new ChangeListener<Number>() {
            //The changed(...) method is called every time a change in the height is detected
            @Override
            public void changed(ObservableValue<? extends Number> value, Number number, Number t1) {
                double tentativeFont = font * Math.sqrt(pane.getHeight()/ label.getHeight());
                if (tentativeFont < font*(100-error)/100 || tentativeFont > font*(100+error)/100) {
                    font = tentativeFont;
                    label.setStyle("-fx-font-size: " + font);
                }
            }
        });
    }
}
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<Pane fx:id="pane" prefHeight="275" prefWidth="800" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/11.0.1" fx:controller="sample.Controller">
    <HBox>
        <Label fx:id="label" text="${input.text}" wrapText="true" prefWidth="400" />
        <TextField text="enter stuff" fx:id="input"/>
    </HBox>
</Pane>

在测试时,我发现错误可能需要调整。当文本很少时,大错误效果很好,而文本很多时,小错误效果很好。你可能也想编程。