Aligning text on Android

Marcin Korniluk
3 min readMar 10, 2016

--

Typography is very complex. Even if we’re talking only about the technical aspects of fonts — metrics, shapes and aligning. Material Design focuses on typography and proposes text alignment as a tool for creating nice layouts. Android doesn’t help much in that matter.

Let’s start with some research: how exactly a font is constructed and how to measure it.

https://en.wikipedia.org/wiki/Baseline_(typography)

http://developer.android.com/reference/android/text/TextPaint.html

Are you done skimming these documents? The most important knowledge is:

  • baseline is the line the text sits on
  • cap height is the line that “square” capital letters reach
  • Android gives only the baseline for free

Now you know that to properly construct Material Design layouts we need a way to align widgets using the cap height and baseline properties of a text. Let’s introduce a marker — a view which can be correctly positioned and aligned in Material layouts. A marker is a rectangle of size (0 — text width) x (cap height — baseline). To make it exactly this size we can use Paint.getTextBounds(…) and measure one of “square” letters. For example ‘I’ or ‘H’. The code is as simple as this:

public class TextMarker extends View {
Paint paint;
Rect rect = new Rect();

// constructors

public void setPaint(Paint paint) {
this.paint = paint;
}

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
paint.getTextBounds("I", 0, 1, rect);
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(rect.height(), MeasureSpec.EXACTLY));
}

public int getBaseline() {
return getHeight();
}

public void draw(Canvas canvas) {
if (isInEditMode())
super.draw(canvas);
}
}

The code can be found here: https://gist.github.com/ZieIony/31f46e61163ca590a48a

Now we can create a layout with these markers. It has to be a RelativeLayout, because we’ll be using the align_baseline attribute. Let’s create the layout described here: https://www.google.com/design/spec/components/lists.html#lists-specs

<RelativeLayout
android:background="#ffcccccc"
android:clickable="true"
android:clipToPadding="false"
android:layout_height="72dp"
android:layout_width="match_parent"
android:paddingLeft="16dp"
android:paddingRight="16dp"
>

<com.example.TextMarker
android:background="#ffff0000"
android:id="@+id/marker1"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_width="match_parent"
/>

<TextView
android:id="@+id/textView1"
android:layout_alignBaseline="@+id/marker1"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_width="match_parent"
android:text="Primary text"
android:textSize="16sp"
/>

<com.example.TextMarker
android:background="#ffff0000"
android:id="@+id/marker2"
android:layout_alignParentBottom="true"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:layout_width="match_parent"
/>

<TextView
android:id="@+id/textView2"
android:layout_alignBaseline="@+id/marker2"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_width="match_parent"
android:text="Secondary text"
android:textSize="14sp"
/>
</RelativeLayout>

You need to set Paint objects for markers. Each marker needs a Paint object taken from a corresponding TextView. TextViews can now be aligned to markers using layout_alignBaseline, layout_alignLeft and layout_alignRight. Other layout attributes aren’t really needed. As a result we get a nicely aligned, low-effort layout that is compatible with Material Design. The markers:

And the final layout:

What’s next? Setting Paint in code is not very convenient. It would be nice to have an attribute which could “borrow” TextView’s Paint object used for final text drawing. Another idea is to copy TextView’s attributes used for setting textSize, textStyle, etc.

Originally published at androidreclib.wordpress.com on March 10, 2016.

--

--