如果超限,调整 JavaFX 标签的大小

Resize JavaFX Label if overrun

我在 TitledPane 的 GridPane 中有一个标签。我希望它在超限时逐步缩小 0.05em,所以三个点 ("Long Labe...") 不会出现 -> "Long Label" 小。

Label 的 isOverrun() 方法会很棒,但 JavaFX 不提供该方法,生活也不是如愿以偿的音乐会。
到目前为止我的工作范围是:

    Bounds tpBounds = tPane.getBoundsInLocal();
    Bounds lblBounds = label.getBoundsInLocal();
    Double fontSize = 1.0;

    while (tpBounds.getWidth() < lblBounds.getWidth() && fontSize > 0.5) {
        fontSize = fontSize-0.05;
        label.setStyle("-fx-font-size: "+fontSize+"em;");

        System.out.println(fontSize+" "+tpBounds.getWidth()+" "+lblBounds.getWidth());
    }

问题:在 while 循环期间,bounds.getWidth() 始终显示原始宽度。新字体大小的 "new" 宽度刷新速度不够快,无法被 while-condition 捕获,因此字体大小越来越小。
任何解决方案?

edit
我更常问:让 Label 自己缩小尺寸真的有那么难,直到它适合而不截断吗?!

解决方案:

fontVerkleinerung(lblXYZ);   //call resizing at beginning


 lblXYZ.styleProperty().addListener((observable, oldV, newV) -> {
    fontVerkleinerung(lblXYZ);   //check size again if resized
 }); 


private void fontVerkleinerung(Label label) {
    Platform.runLater(() -> {
        tpBounds = tPane.getBoundsInLocal();
        if (label.getBoundsInLocal().getWidth()>tpBounds.getWidth() && !fontSizeFits) {
            fontSize = fontSize-0.02;
            label.setStyle("-fx-font-size: "+fontSize+"em;");
        }

        if (label.getBoundsInLocal().getWidth()<=tpBounds.getWidth() && !fontSizeFits) {
            fontSizeFits = true;
        }
    });
}

此解决方法并非 100% 令人满意,GUI 由于 label.setStyle("...") 上的图形更新速度缓慢而受到影响;
styleProperty-listener 开始需要几毫秒。
我已经尝试修复这个问题几个星期了,总能找到 probably-solution 但它没有用.我希望有人能从中获利 post.

这是受内部 api 启发的 hack。

JavaFX 使用 com.sun.javafx.scene.control.skin.Utils class 进行各种基于文本的计算。这还包括计算超限文本到原始文本的剪辑量以及显示省略号文本的位置等。

对于非换行和非多行标签文本,这里只使用了3种方法class:

static String computeClippedText(Font font, String text, double width, OverrunStyle type, String ellipsisString)
static double computeTextWidth(Font font, String text, double wrappingWidth) {...}
static int computeTruncationIndex(Font font, String text, double width) {...}

因为这个 class 是一个内部 api,我只是将这 3 个方法(连同必要的 class 变量)复制到我自己的 Utils class 并用作:

@Override
public void start( Stage primaryStage )
{
    final Label label = new Label( "Lorem Ipsum is simply dummy long text of the printing and typesetting industry" );
    label.setFont( Font.font( 10 ) );
    System.out.println( "originalText = " + label.getText() );

    Platform.runLater( () -> 
        {
            Double fontSize = label.getFont().getSize();
            String clippedText = Utils.computeClippedText( label.getFont(), label.getText(), label.getWidth(), label.getTextOverrun(), label.getEllipsisString() );
            Font newFont = label.getFont();

            while ( !label.getText().equals( clippedText ) && fontSize > 0.5 )
            {
                System.out.println( "fontSize = " + fontSize + ", clippedText = " + clippedText );
                fontSize = fontSize - 0.05;
                newFont = Font.font( label.getFont().getFamily(), fontSize );
                clippedText = Utils.computeClippedText( newFont, label.getText(), label.getWidth(), label.getTextOverrun(), label.getEllipsisString() );
            }

            label.setFont( newFont );
    } );

    Scene scene = new Scene( new VBox(label), 350, 200 );
    primaryStage.setScene( scene );
    primaryStage.show();
}

根据您的要求,您可以进一步简化逻辑并改进 computeClippedText() 方法的计算时间。

另一种有效的方法是:

.setMinHeight(Region.USE_PREF_SIZE)

简单,比计算简单多了。

这个函数很神奇,基本上计算当前标签宽度的计算大小 preferedWidth(可以根据标签的设置进行更改)如果它更大则将字体减少 0.5(也可以是可选的)自定义值)。

注意:如果字体小于0.5也考虑设置一个条件,以避免字体达不到0。也可以用while而不是递归函数转换,也将是最优的,我将两者都设置以防万一

public Label ResizeTextIfOverrunRecursive(Label label, String text, double size) throws Exception {
    FontLoader fontLoader = Toolkit.getToolkit().getFontLoader();
    label.setFont(Font.font(size));
    double font = label.getFont().getSize();
    label.setStyle("-fx-font-size:" + (font) +"px;");
    label.applyCss();
    label.layout();
    label.setText(text);
    double prefWidth = label.getPrefWidth();
    if (fontLoader.computeStringWidth(label.getText(), label.getFont()) > prefWidth){
        return ResizeTextIfOverrunRecursive(label, text, size - 0.5);
    } else return label;
}

public void ResizeTextIfOverrunItaration(Label label, String text, double size) throws Exception {
    FontLoader fontLoader = Toolkit.getToolkit().getFontLoader();
    label.setFont(Font.font(size));
    double font = label.getFont().getSize();
    label.setStyle("-fx-font-size:" + (font) +"px;");
    label.applyCss();
    label.layout();
    double prefWidth = label.getPrefWidth();
    while (fontLoader.computeStringWidth(label.getText(), label.getFont()) > prefWidth){
        font -= 0.5;
        label.setStyle("-fx-font-size:" + (font) +"px;");
        label.applyCss();
        label.layout();
    }

    label.setText(text);
}