Showing posts with label base line. Show all posts
Showing posts with label base line. Show all posts

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.
         TEXT_PAINT.setColor(Color.WHITE);
        //Drawing the text at middle of the view
canvas.drawText(LONG_STRING, 0, getHeight()/2, TEXT_PAINT);
TEXT_PAINT.setColor(Color.RED);
//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.setColor(Color.BLUE);
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).
TEXT_PAINT.setColor(Color.WHITE);
 //Create a static layout to draw 
StaticLayout staticLayout = new StaticLayout(
LONG_STRING, 0, LONG_STRING.length(),
TEXT_PAINT,
                                                 getWidth(),android.text.Layout.Alignment.ALIGN_CENTER , 
1.0f, 1.0f, false);
staticLayout.draw(canvas);


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++){
TEXT_PAINT.setColor(Color.BLUE);
canvas.drawLine(0, staticLayout.getLineTop(i), getWidth(), staticLayout.getLineTop(i), TEXT_PAINT);
TEXT_PAINT.setColor(0x7FFFFFFF&Color.YELLOW);
canvas.drawRect(staticLayout.getLineLeft(i), staticLayout.getLineTop(i), staticLayout.getLineRight(i), staticLayout.getLineBottom(i), TEXT_PAINT);
TEXT_PAINT.setColor(Color.RED);
canvas.drawLine(0, staticLayout.getLineBaseline(i), getWidth(), staticLayout.getLineBaseline(i), TEXT_PAINT);
TEXT_PAINT.setColor(Color.MAGENTA);
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:
Thanks


Thursday, September 15, 2011

Android: Aligning Custom Views at Base Line

Have you ever try to align a non-symmetric custom view at a custom base line as follows?


I tried it using base line attribute of Relative Layout.
First adjust the size of view:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//Height should be always width * 7 / 12
setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth()*7/12);
}


Then Override the getBaseLine method:
@Override
public int getBaseline() {
//base line is from bottom to width/12 (radius of the blue circle)
return getMeasuredHeight()-getMeasuredWidth()/12;
}


Following onDraw method would explain the reason for the above calculation:

@Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setAntiAlias(true);
//Draw the half circle
paint.setColor(Color.GRAY);
float width = getWidth();
float height = getHeight();
canvas.save();
canvas.translate(width/2, height-width/12);
canvas.drawArc(new RectF(-width/3, -width/3, +width/3, width/3), 0, -180, true, paint);
//Draw small circles
paint.setColor(Color.DKGRAY);
RectF rectF = new RectF(-width/2+width/24, -width/12+width/24, -width/3-width/24,                +width/12-width/24);
canvas.drawOval(rectF, paint);
for(int i = 0 ; i < 15 ;i++){
canvas.rotate(12f);
canvas.drawOval(rectF, paint);
}
//Draw blue circle
paint.setColor(Color.BLUE);
canvas.rotate(-180+30);
canvas.drawOval(new RectF(-width/2, -width/12, -width/3, +width/12), paint);
//Draw red arrow
paint.setColor(Color.RED);
Path path = new Path();
path.moveTo(-width*5/12, 0);
path.lineTo(10, -width/12);
path.lineTo(0, 0);
path.lineTo(10, +width/12);
path.close();
canvas.drawPath(path, paint);
canvas.restore();
}


Finally, create a layout xml as following:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<demo.ui.testing.DemoUITesting
android:layout_height="fill_parent" android:layout_width="fill_parent"
android:layout_alignBaseline="@+id/radioButton1"
android:layout_toRightOf="@+id/radioButton1">
<RadioButton android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/radioButton1"
android:layout_centerVertical="true">
</RelativeLayout>

Enjoy!.