Ответ 1
Как насчет paint.getTextBounds() (метод объекта)
Любой прямой способ измерения высоты текста?
То, как я делаю это сейчас, - это использовать Paint measureText()
для получения ширины, а затем путем проб и ошибок найти значение, чтобы получить приблизительную высоту. Я тоже возился с FontMetrics
, но все это похоже на приближенные методы, которые сосут.
Я пытаюсь масштабировать вещи для разных разрешений. Я могу это сделать, но в итоге я получаю невероятно подробный код с большим количеством вычислений, чтобы определить относительные размеры. Я ненавижу это! Должен быть лучший способ.
Как насчет paint.getTextBounds() (метод объекта)
@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
Вертикальное выравнивание по центру текста означает, что вертикальный центр выравнивает ограничивающий прямоугольник, что отличается для разных текстов (колпачки, длинные буквы и т.д.). Но то, что мы на самом деле хотим сделать, это также выровнять исходные тексты визуализированных текстов, чтобы они не выглядели повышенными или рифлеными. Итак, до тех пор, пока мы знаем центр наименьшей буквы (например, "а), мы можем повторно использовать его выравнивание для остальных текстов. Это центрирует выравнивание всех текстов, а также выравнивание по исходному уровню.
Существуют различные способы измерения высоты в зависимости от того, что вам нужно.
Если вы делаете что-то вроде центрирования небольшого количества фиксированного текста, вы, вероятно, захотите getTextBounds
. Вы можете получить ограничивающий прямоугольник следующим образом:
Rect bounds = new Rect();
mTextPaint.getTextBounds(mText, 0, mText.length(), bounds);
int height = bounds.height();
Как вы можете видеть для следующих изображений, разные строки будут иметь разную высоту (показано красным цветом).
Эти разные высоты могут быть недостатком в некоторых ситуациях, когда вам просто нужна постоянная высота независимо от текста. См. Следующий раздел.
Вы можете рассчитать высоту шрифта из метрик шрифта. Высота всегда одна и та же, потому что она получена из шрифта, а не какой-либо конкретной текстовой строки.
Paint.FontMetrics fm = mTextPaint.getFontMetrics();
float height = fm.descent - fm.ascent;
Базовая линия - это строка, на которой сидит текст. Спуск, как правило, самый дальний персонаж будет идти ниже линии, и восхождение, как правило, самый дальний персонаж будет идти выше линии. Чтобы получить высоту, вы должны вычесть подъем, потому что это отрицательное значение. (Базовая линия y=0
и y
уменьшает экран.)
Посмотрите на следующее изображение. Высота для обеих строк равна 234.375
.
Если вам нужна высота строки, а не только высота текста, вы можете сделать следующее:
float height = fm.bottom - fm.top + fm.leading; // 265.4297
Это bottom
и top
строки. Ведущий (интерлайн-интервал) обычно равен нулю, но вы все равно должны его добавлять.
Изображения, приведенные выше, этот проект. Вы можете поиграть с ним, чтобы увидеть, как работают показатели шрифтов.
Для измерения высоты многострочного текста вы должны использовать StaticLayout
. Я подробно об этом говорил в этом ответе, но основной способ получить эту высоту выглядит следующим образом:
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();
Высота - это размер текста, который вы указали в переменной Paint.
Другой способ узнать высоту -
mPaint.getTextSize();
Вы можете просто получить размер текста для объекта 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();
Вы можете использовать класс android.text.StaticLayout
, чтобы указать необходимые границы, а затем вызвать getHeight()
. Вы можете нарисовать текст (содержащийся в макете), вызвав его метод draw(Canvas)
.
Вы должны использовать Rect.width()
и Rect.Height()
, которые возвращаются из getTextBounds()
. Это работает для меня.
Если у кого-то еще есть проблемы, это мой код.
У меня есть пользовательский вид, который является квадратным (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();
}
}