Измерение высоты текста для рисования на холсте (Android) - PullRequest
112 голосов
/ 07 сентября 2010

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

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

Ответы [ 8 ]

142 голосов
/ 07 февраля 2017

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

getTextBounds

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

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

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

enter image description here

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

Paint.FontMetrics

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

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 строки.Начало (межстрочный интервал) обычно равно нулю, но вы все равно должны его добавить.

Изображения выше взяты из этого проекта .Вы можете поэкспериментировать с ним, чтобы увидеть, как работают метрики шрифта.

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(); 
126 голосов
/ 27 сентября 2010

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

82 голосов
/ 02 августа 2012

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

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

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

Хотя ширина текста не такая уж большая проблема, а высота -

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

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

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

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

15 голосов
/ 17 июня 2014

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

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

mPaint.getTextSize();
3 голосов
/ 15 марта 2013

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

2 голосов
/ 15 декабря 2012

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

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

0 голосов
/ 07 июня 2013

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

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