December 23, 2012

Android Tricks: InsetTextView

By

Inset styling is a useful effect, especially on text. It is well-suited for secondary UI elements, because it makes them appear less prominent. In one of my own applications, I recently used inset text to improve the discoverability of a particular feature—informing the user that they can drag nearby user interface elements:

drag-us

The following is the code that I used to implement the inset text style:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using Android.Graphics;
namespace Foo
{
	public class InsetTextView : View
	{
		const int DefaultFontSize = 35;
		const float Offset = 1;
		string text;
		Paint textPaint;
		Paint lightPaint;
		Paint darkPaint;
		public InsetTextView (Context context) : base (context)
		{
			Initialize ();
		}
		public InsetTextView (Context context, IAttributeSet attrs) :
			base (context, attrs)
		{
			text = attrs.GetAttributeValue (null, "text");
			Initialize ();
		}
		public InsetTextView (Context context, IAttributeSet attrs, int defStyle) :
			base (context, attrs, defStyle)
		{
			text = attrs.GetAttributeValue (null, "text");
			Initialize ();
		}
		private void Initialize ()
		{
			textPaint = new Paint () {
				Color = Color.Rgb (0xe9, 0xe9, 0xe9),
				AntiAlias = true,
				TextAlign = Paint.Align.Center,
				TextSize = DefaultFontSize
			};
			textPaint.SetTypeface (Typeface.DefaultBold);
			lightPaint = new Paint () {
				Color = Color.Argb (0xfa, 0xff, 0xff, 0xff),
				AntiAlias = true,
				TextAlign = Paint.Align.Center,
				TextSize = DefaultFontSize
			};
			lightPaint.SetTypeface (Typeface.DefaultBold);
			darkPaint = new Paint () {
				Color = Color.Argb (0x30, 0, 0, 0),
				AntiAlias = true,
				TextAlign = Paint.Align.Center,
				TextSize = DefaultFontSize
			};
			darkPaint.SetTypeface (Typeface.DefaultBold);
		}
		public string Text {
			get {
				return text;
			}
			set {
				text = value ?? string.Empty;
				Invalidate ();
			}
		}
		public Color TextColor {
			get {
				return textPaint.Color;
			}
			set {
				textPaint.Color = value;
				Invalidate ();
			}
		}
		protected override void OnMeasure (int widthMeasureSpec, int heightMeasureSpec)
		{
			var width = (int)Math.Ceiling (textPaint.MeasureText (text)) + 2;
			var metrics = textPaint.GetFontMetricsInt ();
			var height = -metrics.Top + metrics.Bottom + 2;
			SetMeasuredDimension (width, height);
		}
		protected override void OnDraw (Canvas canvas)
		{
			var hCenter = canvas.Width / 2;
			var vCenter = canvas.Height - (textPaint.GetFontMetricsInt ().Bottom + 1);
			canvas.DrawText (text, hCenter - Offset, vCenter - Offset, darkPaint);
			canvas.DrawText (text, hCenter + Offset, vCenter + Offset, lightPaint);
			canvas.DrawText (text, hCenter, vCenter, textPaint);
		}
	}
}

A “real” inset effect implies inner glow and proper shading, but I chose to use a simpler approach. I used solid colors in a shading palette and layered three instances of the text.

You can use InsetTextView in XML or in code. The following XML snippet demonstrates how it can be used in an Android user interface layout:

<Foo.InsetTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_marginTop="16dp"
    text="Hey Oh" />
TwitterFacebookGoogle+LinkedInEmail