Come ottenere altezza reale stringa in Java?
Domanda
sto usando FontMetrics.getHeight()
per ottenere l'altezza della stringa, ma mi dà un valore errato, tagliando i discendenti dei caratteri della stringa. C'è una funzione meglio posso usare?
Soluzione
Il metodo getStringBounds()
di seguito si basa sulla GlyphVector
per la corrente Graphics2D
carattere, che funziona molto bene per una stringa di riga di testo:
public class StringBoundsPanel extends JPanel
{
public StringBoundsPanel()
{
setBackground(Color.white);
setPreferredSize(new Dimension(400, 247));
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// must be called before getStringBounds()
g2.setFont(getDesiredFont());
String str = "My Text";
float x = 140, y = 128;
Rectangle bounds = getStringBounds(g2, str, x, y);
g2.setColor(Color.red);
g2.drawString(str, x, y);
g2.setColor(Color.blue);
g2.draw(bounds);
g2.dispose();
}
private Rectangle getStringBounds(Graphics2D g2, String str,
float x, float y)
{
FontRenderContext frc = g2.getFontRenderContext();
GlyphVector gv = g2.getFont().createGlyphVector(frc, str);
return gv.getPixelBounds(null, x, y);
}
private Font getDesiredFont()
{
return new Font(Font.SANS_SERIF, Font.BOLD, 28);
}
private void startUI()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) throws Exception
{
final StringBoundsPanel tb = new StringBoundsPanel();
SwingUtilities.invokeAndWait(new Runnable()
{
public void run()
{
tb.startUI();
}
});
}
}
Si noti che ho omesso le importazioni per chiarezza.
Il risultato:
Altri suggerimenti
Cosa ti fa pensare che restituisce il valore errato? E 'molto più probabile che le vostre aspettative di ciò che ritorna non corrisponde alla specifica. Si noti che è perfettamente bene se alcuni glifi nel font andare sopra o sotto quei valori.
getMaxDescent()
e getMaxAscent()
dovrebbe dirvi i valori massimi assoluti di quei campi per qualsiasi glifo del font.
Se volete conoscere le metriche per una stringa specifica, allora è sicuramente desidera chiamare getLineMetrics()
.
FontMetrics.getAscent () e FontMetrics.getDescent () potrebbe fare il trucco.
Di recente ho scritto il codice qui sotto come avevo bisogno misurazioni di altezza Perfect Pixel per le gamme specifiche del tipo di carattere (ad esempio: tutti i personaggi minori, o tutti i numeri).
Se avete bisogno di un codice più veloce (il mio ha per i cicli) mi sento di raccomandare in esecuzione una volta alla start-up per ottenere tutti i valori (ad esempio, da 1 a 100) in un array e quindi utilizzare la matrice al posto.
Il codice disegna praticamente tutti i caratteri della stringa di input nello stesso luogo sovrapposto su un 250x250 bitmap (aumentare o ridurre, se necessario), si comincia alla ricerca di pixel dall'alto, quindi dal basso, quindi restituisce l'altezza massima trovata. Funziona con stringhe normali anche se è stato progettato per intervalli di caratteri. Ciò significa che c'è una sorta di ridondanza nella valutazione stringhe regolari come alcuni dei personaggi ripetere. Quindi, se la stringa imput supera il conteggio alfabeto (26), utilizzare come 'Trangé' imput: "ABCD ... z" e altri personaggi che possono essere utilizzati. E 'più veloce.
La speranza che aiuta.
public int getFontPixelHeight(float inSize, Paint sourcePaint, String tRange)
{
// It is assumed that the font is already set in the sourcePaint
int bW = 250, bH = 250; // bitmap's width and height
int firstContact = -1, lastContact = -2; // Used when scanning the pixel rows. Initial values are set so that if no pixels found, the returned result is zero.
int tX = (int)(bW - inSize)/2, tY = (int)(bH - inSize)/2; // Used for a rough centering of the displayed characters
int tSum = 0;
// Preserve the original paint attributes
float oldSize = sourcePaint.getTextSize();
int oldColor = sourcePaint.getColor();
// Set the size/color
sourcePaint.setTextSize(inSize); sourcePaint.setColor(Color.WHITE);
// Create the temporary bitmap/canvas
Bitmap.Config bConf = Bitmap.Config.ARGB_8888;
Bitmap hld = Bitmap.createBitmap(250, 250, bConf);
Canvas canv = new Canvas(hld);
for (int i = 0; i < bH; i++)
{
for (int j = 0; j < bW; j++)
{
hld.setPixel(j, i, 0); // Zero all pixel values. This might seem redundant, but I am not quite sure that creating a blank bitmap means the pixel color value is indeed zero, and I need them to be zero so the addition performed below is correct.
}
}
// Display all characters overlapping at the same position
for (int i = 0; i < tRange.length(); i++)
{
canv.drawText("" + tRange.charAt(i), tX, tY, sourcePaint);
}
for (int i = 0; i < bH; i++)
{
for (int j = 0; j < bW; j++)
{
tSum = tSum + hld.getPixel(j, i);
}
if (tSum > 0) // If we found at least a pixel, save row index and exit loop
{
firstContact = i;
tSum = 0; // Reset
break;
}
}
for (int i = bH - 1; i > 0 ; i--)
{
for (int j = 0; j < bW; j++)
{
tSum = tSum + hld.getPixel(j, i);
}
if (tSum > 0) // If we found at least a pixel, save row index and exit loop
{
lastContact = i;
break;
}
}
// Restore the initial attributes, just in case the paint was passed byRef somehow
sourcePaint.setTextSize(oldSize);
sourcePaint.setColor(oldColor);
return lastContact - firstContact + 1;
}
getHeight()
non può tagliare i discendenti di una stringa, solo disegnando la stringa può farlo. Si utilizza l'altezza tornato da getHeight
per disegnare la stringa in qualche modo, e probabilmente si sta abusando l'altezza. Ad esempio, se si posiziona il punto di inizio della stringa in fondo a una scatola che è getHeight () alto invece, la linea di base del testo siederà sul bordo inferiore della scatola, e molto probabilmente verrà troncato i discendenti.
geometria Text è un argomento complesso, infuso con reperti storici bizzarre. Come altri hanno suggerito, utilizzare getAscent
e getDescent
per cercare di posizionare la linea di base correttamente all'interno del vostro box.