Friday, June 29, 2012

Android: Understating Text Drawing

When it comes to draw text inside our custom view, it is difficult to position text vertically in correct place if we use canvas.drawText(..,x,y..) , because it will not start to draw from the give y coordinate rather it will position the text base line in the give y coordinate.

First look at this following image:

In the first view (view 01) text and a red line are drawn in the middle vertically.
        //Drawing the text at middle of the view
canvas.drawText(LONG_STRING, 0, getHeight()/2, TEXT_PAINT);
//Here we draw the middle line
canvas.drawLine(0, getHeight()/2, getWidth(), getHeight()/2, TEXT_PAINT);

If you think that, the text is drawn on top of the line, then simply if we find an API to get the height of the text then we can align use that value.
Rect rect = new Rect();
TEXT_PAINT.getTextBounds(LONG_STRING, 0, LONG_STRING.length(), rect);
//Measure the height and draw line on top of text
canvas.drawLine(0, getHeight()/2-rect.height(), getWidth(), getHeight()/2-rect.height(), TEXT_PAINT);

But that is not the case, you see, some part of 'y' from 'Please try this is...' is drawn down of the middle line. So we want to know how actually text are drawn and actual center of the text.

To understand this I used StaticLayout in the second view (view 02).
 //Create a static layout to draw 
StaticLayout staticLayout = new StaticLayout(
                                                 getWidth(),android.text.Layout.Alignment.ALIGN_CENTER , 
1.0f, 1.0f, false);

It provides necessary API to get top, bottom and base line of the text. In the second view you can see those lines.
for(int i = 0 ; i < staticLayout.getLineCount();i++){
canvas.drawLine(0, staticLayout.getLineTop(i), getWidth(), staticLayout.getLineTop(i), TEXT_PAINT);
canvas.drawRect(staticLayout.getLineLeft(i), staticLayout.getLineTop(i), staticLayout.getLineRight(i), staticLayout.getLineBottom(i), TEXT_PAINT);
canvas.drawLine(0, staticLayout.getLineBaseline(i), getWidth(), staticLayout.getLineBaseline(i), TEXT_PAINT);
canvas.drawLine(0, staticLayout.getLineBottom(i), getWidth(), staticLayout.getLineBottom(i), TEXT_PAINT);

Now let use this values to draw text in proper place as shown in view 03.
canvas.drawText(LONG_STRING, 0, getHeight()/2+(staticLayout.getLineBaseline(0)-staticLayout.getLineBottom(0)/2), TEXT_PAINT);

I have uploaded sample source code here:

1 comment:

Tomek said...

Thanks a lot :) example with StaticLayout is very useful.