Measuring Text Height to Be Drawn on Canvas ( Android )

Measuring text height to be drawn on Canvas ( Android )

What about paint.getTextBounds() (object method)

Android: Measure Text Height on a Canvas

I think it is because the "p" in "compute" extends below the baseline whereas "Hello World" only contains letters that are above the baseline.

Since the line distance should not depend on what specific letters your text happens to consist of you are probably looking for Paint.FontMetrics which can be obtained via Paint.getFontMetrics(). Compute descent - ascent + leading to get the recommended baseline distance (because ascent has a negative value).

measuring text on scaled canvas

Ok, just found out how to circumvent the issue.

The problem is that the Paint does not know about the Canvas scaling. Therefore measureText and getTextBounds deliver the unscaled result. But since the font size does not scale linearly (however, the drawn rect does ), you have to make up for that effect manually.

So the solution would be:

// paint the text as usual
Paint paint = new Paint();
paint.setTypeface(font);
paint.setTextSize(fontSize);
canvas.drawText(text, x, y, paint);

// measure the text using scaled font size and correct the scaled value afterwards
Paint paint = new Paint();
paint.setTypeface(font);
paint.setTextSize(fontSize * scaling);
float w = paint.measureText(text) / scaling;

Measuring text width to be drawn on Canvas ( Android )

Have you looked at android.graphics.Paint#measureText(String txt)?

Measuring tight bounds box of multiline text

Build the StaticLayout then determine the bounds of each individual line using methods from TextPaint. The boundaries for the entire multi-line text is the top boundary of the top line, the bottom boundary of the last line and the left-most and right-most boundaries of all the lines.

Here is a sample custom view that demonstrate this concept. The layout is simply the custom view with a width of 200dp and a height of wrap_content.

Sample Image

Sample Image

MyStaticText

public class MyStaticText extends View {
private final String mText = "This is some longer text to test multiline layout.";
private TextPaint mTextPaint;
private StaticLayout mStaticLayout;
private final Paint mRectPaint = new Paint();

public MyStaticText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

private void init() {
mTextPaint = new TextPaint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(FONT_SIZE * getResources().getDisplayMetrics().density);
mTextPaint.setColor(Color.BLACK);

mRectPaint.setStyle(Paint.Style.STROKE);
mRectPaint.setStrokeWidth(2f);
mRectPaint.setColor(Color.RED);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthAskedFor = MeasureSpec.getSize(widthMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
mStaticLayout = new StaticLayout(mText, mTextPaint, widthAskedFor, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0, false);
} else {
throw new RuntimeException("View width must be exactly specified.");
}

int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int height;
if (heightMode == MeasureSpec.AT_MOST) {
height = mStaticLayout.getHeight() + getPaddingTop() + getPaddingBottom();
} else {
throw new RuntimeException("View height must be 'wrap_content'.");
}
setMeasuredDimension(widthAskedFor, height);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

mStaticLayout.draw(canvas);

// Start with bounds of first line.
Rect textBounds = getLineBounds(0);

// Check bounds of last line since it will be the bottom of the bounding rectangle.
Rect lineBounds = getLineBounds(mStaticLayout.getLineCount() - 1);
if (lineBounds.bottom > textBounds.bottom) {
textBounds.bottom = lineBounds.bottom;
}

// Now check all lines for min left bound and max right bound.
for (int line = 0; line < mStaticLayout.getLineCount(); line++) {
lineBounds = getLineBounds(line);
if (lineBounds.left < textBounds.left) {
textBounds.left = lineBounds.left;
}
if (lineBounds.right > textBounds.right) {
textBounds.right = lineBounds.right;
}
}
canvas.drawRect(textBounds, mRectPaint);
}

private Rect getLineBounds(int line) {
int firstCharOnLine = mStaticLayout.getLineStart(line);
int lastCharOnLine = mStaticLayout.getLineVisibleEnd(line);
String s = mText.substring(firstCharOnLine, lastCharOnLine);

// bounds will store the rectangle that will circumscribe the text.
Rect bounds = new Rect();

// Get the bounds for the text. Top and bottom are measured from the baseline. Left
// and right are measured from 0.
mTextPaint.getTextBounds(s, 0, s.length(), bounds);
int baseline = mStaticLayout.getLineBaseline(line);
bounds.top = baseline + bounds.top;
bounds.bottom = baseline + bounds.bottom;

return bounds;
}

private static final int FONT_SIZE = 48;
}

Here is a demo app that has a more generalized solution.



Related Topics



Leave a reply



Submit