Изменение размера метки JavaFX при переполнении

У меня есть Метка в GridPane в TitledPane. Я хочу, чтобы он постепенно уменьшался на 0,05 мкм, если он переполнен, поэтому три точки ("Длинная метка...") не отображаются -> "Длинная метка" в маленьком значении.

IsOverrun()- метод для Label был бы хорош, но 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() всегда показывает исходную ширину. "Новая" ширина с новым размером шрифта не обновляется достаточно быстро, чтобы быть пойманной условием while, поэтому размер шрифта становится все меньше и меньше.
Любые решения?

редактировать
Я спрашиваю чаще: действительно ли это сложно сделать ярлык сам по себе уменьшенным, пока он не уместится без усечения?!

3 ответа

Решение

Это хак, вдохновленный внутренним API.

JavaFX использует com.sun.javafx.scene.control.skin.Utils Класс для различных текстовых расчетов. Это также включает в себя подсчет переполненных текстов в зависимости от того, на сколько обрезается исходный текст и где показывается многоточечный текст и т. Д.

Для не обернутых и не многострочных текстов меток в этом классе используются только 3 метода:

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) {...}

Поскольку этот класс является внутренним API, я просто скопировал эти 3 метода (вместе с необходимыми переменными класса) в свой собственный класс Utils и использовал его как:

@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)

Просто и намного проще, чем рассчитать.

Решение:

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.
Я пытался исправить это в течение нескольких недель, всегда находил вероятное решение, но оно не работало. Я надеюсь, что кто-то получит прибыль от этого поста.

Эта функция творит чудеса, в основном вычисляет размер вычисления текущей ширины метки preferredWidth(может быть изменен в зависимости от настройки метки), и если она больше, то уменьшите шрифт на 0,5 (также необязательно, может быть пользовательское значение).

примечание: рассмотреть также установить условие, если шрифт меньше 0,5, чтобы избежать того, что шрифт не достигнет нуля. Также может быть преобразован с помощью 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);
}
Другие вопросы по тегам