2010-09-06 4 views
90

Любой прямой способ измерения высоты текста? То, как я это делаю сейчас, - это использовать Paint measureText(), чтобы получить ширину, затем путем проб и ошибок найти значение, чтобы получить приблизительную высоту. Я тоже возился с FontMetrics, но все это похоже на приближенные методы, которые сосут.Измерение высоты текста на холсте (Android)

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

ответ

117

насчет paint.getTextBounds() (метод объекта)

+1

Это дает очень странные результаты, когда я оценить высоту текста. Короткий текст приводит к высоте 12, тогда как ДЕЙСТВИТЕЛЬНО длинный текст приводит к высоте 16 (с учетом размера шрифта 16). Не имеет для меня никакого смысла (android 2.3.3) – AgentKnopf

+34

Разница в высоте - это то, где у вас есть descenders в тексте, т. Е.«Высокий» выше «Низкий» из-за части g ниже строки – FrinkTheBrave

1

Вы должны использовать Rect.width() и Rect.Height(), которые вернулись из getTextBounds() вместо. Это подходит для меня.

+2

Нет, если вы имеете дело с несколькими сегментами текстов. Причина в моем ответе выше. –

74

@ ответ bramp является правильным - частично, в том, что он не упоминает, что расчетные границы будет минимальный прямоугольник, содержащий текст полностью неявных начальных координат 0, 0

Это означает, что высота, например, «Py» будет отличаться от высоты «py» или «hi» или «oi» или «aw», потому что по пикселям они требуют разной высоты.

Это не является эквивалентом FontMetrics в классической java.

Хотя ширина текста не очень большая, высота есть.

В частности, если вам нужно вертикально центрировать выровненный текст, попробуйте получить границы текста «a» (без кавычек), вместо того, чтобы использовать текст, который вы собираетесь нарисовать. работает для меня ...

Вот что я имею в виду:

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.LINEAR_TEXT_FLAG); 

paint.setStyle(Paint.Style.FILL); 
paint.setColor(color); 
paint.setTextAlign(Paint.Align.CENTER); 
paint.setTextSize(textSize); 

Rect bounds = new Rect(); 
paint.getTextBounds("a", 0, 1, bounds); 

buffer.drawText(this.myText, canvasWidth >> 1, (canvasHeight + bounds.height()) >> 1, paint); 
// remember x >> 1 is equivalent to x/2, but works much much faster 

Вертикально центр выравнивания текста означает вертикальное выравнивание по центру ограничивающего прямоугольника - который различен для различных текстов (шапок, длинных писем и т.д.). Но то, что мы на самом деле хотим сделать, это также выровнять исходные тексты визуализированных текстов, чтобы они не выглядели повышенными или рифлеными. Итак, до тех пор, пока мы знаем центр наименьшей буквы (например, «а), мы можем повторно использовать его выравнивание для остальных текстов. Это позволит выравнивать все тексты, а также выравнивать их по исходным линиям.

+18

Не видели 'x >> 1' для возрастов. Upvoting только для этого :) – keaukraine

+50

Хороший современный компилятор увидит 'x/2' и оптимизирует его до' x >> 1' –

+31

@keaukraine 'x/2' гораздо более дружелюбен при чтении кода, учитывая комментарий Криса. – d3dave

2

Вы можете просто получить размер текста для объекта Paint, используя метод getTextSize(). Например:

Paint mTextPaint = new Paint (Paint.ANTI_ALIAS_FLAG); 
//use densityMultiplier to take into account different pixel densities 
final float densityMultiplier = getContext().getResources() 
      .getDisplayMetrics().density; 
mTextPaint.setTextSize(24.0f*densityMultiplier); 

//... 

float size = mTextPaint.getTextSize(); 
+0

Откуда берутся '24.0f'? – Erigami

+0

24.0f это просто пример размера текста – moondroid

2

Вы можете использовать android.text.StaticLayout класс для определения границы, необходимые, а затем вызвать getHeight(). Вы можете нарисовать текст (содержащийся в макете), вызвав его метод draw(Canvas).

0

Если у кого-то еще есть проблемы, это мой код.

У меня есть пользовательский вид, который является квадратным (width = height), и я хочу назначить ему символ. onDraw() показывает, как получить высоту персонажа, хотя я его не использую. Символ будет отображаться посередине.

public class SideBarPointer extends View { 

    private static final String TAG = "SideBarPointer"; 

    private Context context; 
    private String label = ""; 
    private int width; 
    private int height; 

    public SideBarPointer(Context context) { 
     super(context); 
     this.context = context; 
     init(); 
    } 

    public SideBarPointer(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     this.context = context; 
     init(); 
    } 

    public SideBarPointer(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
     this.context = context; 
     init(); 
    } 

    private void init() { 
//  setBackgroundColor(0x64FF0000); 
    } 

    @Override 
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 

     height = this.getMeasuredHeight(); 
     width = this.getMeasuredWidth(); 

     setMeasuredDimension(width, width); 
    } 

    protected void onDraw(Canvas canvas) { 
     float mDensity = context.getResources().getDisplayMetrics().density; 
     float mScaledDensity = context.getResources().getDisplayMetrics().scaledDensity; 

     Paint previewPaint = new Paint(); 
     previewPaint.setColor(0x0C2727); 
     previewPaint.setAlpha(200); 
     previewPaint.setAntiAlias(true); 

     Paint previewTextPaint = new Paint(); 
     previewTextPaint.setColor(Color.WHITE); 
     previewTextPaint.setAntiAlias(true); 
     previewTextPaint.setTextSize(90 * mScaledDensity); 
     previewTextPaint.setShadowLayer(5, 1, 2, Color.argb(255, 87, 87, 87)); 

     float previewTextWidth = previewTextPaint.measureText(label); 
//  float previewTextHeight = previewTextPaint.descent() - previewTextPaint.ascent(); 
     RectF previewRect = new RectF(0, 0, width, width); 

     canvas.drawRoundRect(previewRect, 5 * mDensity, 5 * mDensity, previewPaint); 
     canvas.drawText(label, (width - previewTextWidth)/2, previewRect.top - previewTextPaint.ascent(), previewTextPaint); 

     super.onDraw(canvas); 
    } 

    public void setLabel(String label) { 
     this.label = label; 
     Log.e(TAG, "Label: " + label); 

     this.invalidate(); 
    } 
} 
+2

-1 для распределений в onDraw(): он принесет тонну преимуществ производительности, если вы должны были объявить поля в классе (инициировать в конструкторе) и повторно использовать их в OnDraw(). – zoltish

+0

@zoltish Спасибо за комментарий, но проголосовать? !! обычно использует для дублирования/неправильных ответов, а не относительные или даже информативные ответы. в любом случае вы правы и правильны для тех, кто использует мое предложение. – Hesam

13

Высота - это размер текста, который вы задали в переменной Paint.

Другой способ узнать высоту является

mPaint.getTextSize(); 
67

Существуют различные способы измерения высоты в зависимости от того, что вам нужно.

getTextBounds

Если вы делаете что-то вроде точно центрирования небольшого количества фиксированного текста, вы, вероятно, хотите getTextBounds. Вы можете получить прямоугольник как этот

Rect bounds = new Rect(); 
mTextPaint.getTextBounds(mText, 0, mText.length(), bounds); 
int height = bounds.height(); 

Как вы можете видеть на следующих изображений, различные строки будут давать разную высоту (показаны красным цветом).

enter image description here

Эти различные высоты может быть недостаток в некоторых ситуациях, когда вы просто не нуждаются в постоянной высоте, независимо от того, каков текст. См. Следующий раздел.

Paint.FontMetrics

Вы можете рассчитать Hight шрифта из метрик шрифта. Высота всегда одна и та же, потому что она получена из шрифта, а не какой-либо конкретной текстовой строки.

Paint.FontMetrics fm = mTextPaint.getFontMetrics(); 
float height = fm.descent - fm.ascent; 

Базовая линия - это линия, на которой сидит текст. Спуск, как правило, самый дальний персонаж будет идти ниже линии, и восхождение, как правило, самый дальний персонаж будет идти выше линии. Чтобы получить высоту, вы должны вычесть подъем, потому что это отрицательное значение. (Исходный уровень y=0 и y прекращает действие экрана.)

Посмотрите на следующее изображение. Высота для обеих строк равна 234.375.

enter image description here

Если вы хотите высоту строки, а не только высоту текста, вы можете сделать следующее:

float height = fm.bottom - fm.top + fm.leading; // 265.4297 

Таковы bottom и top линии. Ведущий (интерлайн-интервал) обычно равен нулю, но вы должны добавить его в любом случае.

Образы вышеприведенные - this project. Вы можете поиграть с ним, чтобы увидеть, как работают функции Font Metrics.

StaticLayout

Для измерения высоты многострочного текста следует использовать StaticLayout. Я говорил об этом более подробно в this answer, но основной способ получить эту высоту, как это:

String text = "This is some text. This is some text. This is some text. This is some text. This is some text. This is some text."; 

TextPaint myTextPaint = new TextPaint(); 
myTextPaint.setAntiAlias(true); 
myTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density); 
myTextPaint.setColor(0xFF000000); 

int width = 200; 
Layout.Alignment alignment = Layout.Alignment.ALIGN_NORMAL; 
float spacingMultiplier = 1; 
float spacingAddition = 0; 
boolean includePadding = false; 

StaticLayout myStaticLayout = new StaticLayout(text, myTextPaint, width, alignment, spacingMultiplier, spacingAddition, includePadding); 

float height = myStaticLayout.getHeight(); 
+0

Хорошее объяснение. Какое приложение - скриншоты? –

+1

@ MichealJohnson, я добавил приложение в качестве [проекта GitHub здесь] (https://github.com/suragch/AndroidFontMetrics). – Suragch

Смежные вопросы