Oblicz szerokość wyświetlania ciągu w Javie

91

Jak obliczyć długość (w pikselach) łańcucha w Javie?

Najlepiej bez Swingu.

EDYCJA: Chciałbym narysować ciąg za pomocą drawString () w Java2D i użyć długości do zawijania tekstu.

eflles
źródło
4
Bez huśtawki? Z jakiego urządzenia korzystasz? Jaka czcionka? Jaki rozmiar? Jaki styl? Wszystkie te rzeczy zmieniają szerokość wyświetlania.
S.Lott
3
Jak zamierzasz narysować sznurek? Z AWT? Lub z innym zestawem narzędzi? Rozmiar ciągu w pikselach zależy od interfejsu API rysowania, który później narysuje piksel (i oczywiście jakiej czcionki używasz i jakiego rozmiaru oraz czy czcionka jest pogrubiona / kursywa itp.). Bez znajomości rysunkowego API i właściwości czcionki, ciąg nie ma żadnego rozmiaru.
Mecki
1
@S. Lott. Masz to w jednym. Kusiło mnie, aby zamknąć to jako nie-kwestię.
David Arno,
1
@David Arno: Jestem softie na n00bz. Dodam tag [początkujący].
S.Lott
Dla odpowiednika .NET jest klasa TextRenderer, patrz stackoverflow.com/questions/604298/…
Spoike

Odpowiedzi:

137

Jeśli chcesz tylko użyć AWT, użyj Graphics.getFontMetrics(opcjonalnie określając czcionkę, dla czcionki innej niż domyślna), aby uzyskać a, FontMetricsa następnie FontMetrics.stringWidthznaleźć szerokość dla określonego ciągu.

Na przykład, jeśli masz Graphicszmienną o nazwie g, użyjesz:

int width = g.getFontMetrics().stringWidth(text);

W przypadku innych zestawów narzędzi musisz podać nam więcej informacji - zawsze będzie to zależne od zestawu narzędzi.

Jon Skeet
źródło
8
Może być jaśniej, jeśli podasz przykładowe użycie, początkowo próbowałem użyć go jako metody statycznej, ponieważ napisałeś to w ten sposób.
Aequitas
Przestarzałe! ale wciąż najlepsza metoda !, Dlaczego odrzucają metodę, skoro nie ma lepszego sposobu !!! Po prostu tego nie rozumiem!
Iman
2
@Zich: Nie jestem pewien, która metoda jest przestarzała - żadna z nich nie jest oznaczona jako przestarzała w dokumentacji Java 8, o ile widzę ...
Jon Skeet
2
@Zich: To metoda Graphics, nie FontMetrics. Ale dzwonisz Toolkit.getFontMetrics, co jest rzeczywiście przestarzałe i nie jest to, o czym mówi ta metoda ... musisz bardzo uważać na tego typu rzeczy, szczególnie zanim zaczniesz mówić o zgłaszaniu błędów ...
Jon Skeet
1
@Zich: Cóż, nie zgadłbym - użyłbym Toolkit.getFontMetricszamiast tego metody, która nie jest przestarzała, lub sugeruje metody, które nie są przestarzałe .
Jon Skeet
55

Nie zawsze musi to być zależne od zestawu narzędzi lub nie zawsze trzeba używać podejścia FontMetrics, ponieważ wymaga najpierw uzyskania obiektu graficznego, którego nie ma w kontenerze internetowym lub w środowisku bezgłowym.

Przetestowałem to w serwlecie sieciowym i oblicza on szerokość tekstu.

import java.awt.Font;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;

...

String text = "Hello World";
AffineTransform affinetransform = new AffineTransform();     
FontRenderContext frc = new FontRenderContext(affinetransform,true,true);     
Font font = new Font("Tahoma", Font.PLAIN, 12);
int textwidth = (int)(font.getStringBounds(text, frc).getWidth());
int textheight = (int)(font.getStringBounds(text, frc).getHeight());

Dodaj niezbędne wartości do tych wymiarów, aby utworzyć wymagany margines.

Olofu Mark
źródło
1
Nie sądzę, aby tworzenie affineTransform i fontRenderContext w ten sposób skutkowało dobrym zachowaniem. Myślę, że font.getTransfrom () byłby bardziej logiczny.
Lyth
1
Twój przykład nadal korzysta z funkcji AWT. Na przykład z czcionką SWT to nie zadziała, dlatego „zawsze będzie zależne od zestawu narzędzi”
serg.nechaev
1
@Olofu Mark - getStringBounds podaje tylko granice logiczne. Aby było lepiej, należy użyć obiektu getBounds () i obiektu LineMetrics do pobrania aktualnej wysokości, w tym ascent + descent.
Jones,
8

Użyj metody getWidth w następującej klasie:

import java.awt.*;
import java.awt.geom.*;
import java.awt.font.*;

class StringMetrics {

  Font font;
  FontRenderContext context;

  public StringMetrics(Graphics2D g2) {

    font = g2.getFont();
    context = g2.getFontRenderContext();
  }

  Rectangle2D getBounds(String message) {

    return font.getStringBounds(message, context);
  }

  double getWidth(String message) {

    Rectangle2D bounds = getBounds(message);
    return bounds.getWidth();
  }

  double getHeight(String message) {

    Rectangle2D bounds = getBounds(message);
    return bounds.getHeight();
  }

}
Ed Poor
źródło
1

A teraz coś zupełnie innego. W poniższym przykładzie przyjęto czcionkę Arial i dokonano dzikiego przypuszczenia na podstawie liniowej interpolacji znaku w funkcji szerokości.

// Returns the size in PICA of the string, given space is 200 and 'W' is 1000.
// see https://p2p.wrox.com/access/32197-calculate-character-widths.html

static int picaSize(String s)
{
    // the following characters are sorted by width in Arial font
    String lookup = " .:,;'^`!|jl/\\i-()JfIt[]?{}sr*a\"ce_gFzLxkP+0123456789<=>~qvy$SbduEphonTBCXY#VRKZN%GUAHD@OQ&wmMW";
    int result = 0;
    for (int i = 0; i < s.length(); ++i)
    {
        int c = lookup.indexOf(s.charAt(i));
        result += (c < 0 ? 60 : c) * 7 + 200;
    }
    return result;
}

Ciekawe, ale może niezbyt praktyczne.

John Henckel
źródło
1

Osobiście szukałem czegoś, co pozwoliłoby mi obliczyć wielowierszowy obszar ciągu, aby móc określić, czy dany obszar jest wystarczająco duży, aby wydrukować ciąg - z zachowaniem określonej czcionki.

private static Hashtable hash = new Hashtable();
private Font font;
private LineBreakMeasurer lineBreakMeasurer;
private int start, end;

public PixelLengthCheck(Font font) {
    this.font = font;
}

public boolean tryIfStringFits(String textToMeasure, Dimension areaToFit) {
    AttributedString attributedString = new AttributedString(textToMeasure, hash);
    attributedString.addAttribute(TextAttribute.FONT, font);
    AttributedCharacterIterator attributedCharacterIterator =
            attributedString.getIterator();
    start = attributedCharacterIterator.getBeginIndex();
    end = attributedCharacterIterator.getEndIndex();

    lineBreakMeasurer = new LineBreakMeasurer(attributedCharacterIterator,
            new FontRenderContext(null, false, false));

    float width = (float) areaToFit.width;
    float height = 0;
    lineBreakMeasurer.setPosition(start);

    while (lineBreakMeasurer.getPosition() < end) {
        TextLayout textLayout = lineBreakMeasurer.nextLayout(width);
        height += textLayout.getAscent();
        height += textLayout.getDescent() + textLayout.getLeading();
    }

    boolean res = height <= areaToFit.getHeight();

    return res;
}
wmioduszewski
źródło