Как получить реальную высоту строки в Java?
Вопрос
я использую FontMetrics.getHeight()
чтобы получить высоту строки, но он дает мне неправильное значение, обрезая нижние элементы строковых символов.Есть ли лучшая функция, которую я могу использовать?
Решение
А getStringBounds()
приведенный ниже метод основан на GlyphVector
на текущий момент Graphics2D
шрифт, который очень хорошо работает для одной строки текста:
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();
}
});
}
}
Обратите внимание: для ясности я опустил импорт.
Результат:
Другие советы
Что заставляет вас думать, что он возвращает неправильное значение?Гораздо более вероятно, что ваши ожидания того, что он вернет, не соответствуют спецификации.Обратите внимание: совершенно нормально, если некоторые глифы в шрифте выходят за эти значения или занижают их.
getMaxDescent()
и getMaxAscent()
должен сообщить вам абсолютные максимальные значения этих полей для любого глифа в шрифте.
Если вы хотите узнать метрики для конкретной строки, вам обязательно нужно позвонить getLineMetrics()
.
FontMetrics.getAscent() и FontMetrics.getDescent() может сработать.
Недавно я написал приведенный ниже код, так как мне нужны были точные измерения высоты в пикселях для определенных диапазонов шрифта (например:все младшие символы или все цифры).
Если вам нужен более быстрый код (у меня есть циклы for), я бы рекомендовал запустить его один раз при запуске, чтобы получить все значения (например, от 1 до 100) в массиве, а затем вместо этого использовать массив.
Код в основном рисует все символы из входной строки в одном и том же месте, перекрывая растровое изображение 250x250 (при необходимости увеличивая или уменьшая), он начинает искать пиксели сверху, затем снизу, а затем возвращает максимальную найденную высоту.Он работает с обычными строками, даже если он был разработан для диапазонов символов.Это означает, что при оценке обычных строк возникает своего рода избыточность, поскольку некоторые символы повторяются.Поэтому, если ваша входная строка превышает количество алфавитов (26), используйте в качестве входных данных tRange:«abcd...z» и другие символы, которые можно использовать.Это быстрее.
Надеюсь, это поможет.
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()
невозможно отрезать нижние элементы строки, это можно сделать только путем рисования строки.Вы используете высоту, возвращенную из getHeight
чтобы как-то нарисовать строку, и, вероятно, вы неправильно используете высоту.Например, если вы разместите начальную точку строки внизу блока с высотой getHeight(), то базовая линия вашего текста будет находиться на нижнем крае блока, и, скорее всего, нижние элементы будут обрезаны.
Геометрия текста — сложная тема, наполненная причудливыми историческими артефактами.Как предлагали другие, используйте getAscent
и getDescent
чтобы попытаться правильно расположить базовую линию внутри рамки.