Измерение высоты текста на Canvas (Android)

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

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

Ответ 2

@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

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

Ответ 3

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

getTextBounds

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

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

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

введите описание изображения здесь

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

Paint.FontMetrics

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

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

Для измерения высоты многострочного текста вы должны использовать 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(); 

Ответ 4

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

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

mPaint.getTextSize();

Ответ 5

Вы можете просто получить размер текста для объекта 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();

Ответ 6

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

Ответ 7

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

Ответ 8

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

У меня есть пользовательский вид, который является квадратным (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();
    }
}