MobileAndroidAndroid Watch Faces Programming Tutorial

Android Watch Faces Programming Tutorial content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

If you are completely new to Android Wear development, it is highly recommended you start from another short tutorial, “Building Your First Wearable Android App”. You will learn how to set up for mobile and wearable devices as well as configure for Android Studio.

This tutorial will focus on creating the most widely used feature on smart watches: a personalized watch face from a software developer’s perspective. It includes how to prepare the implementation of a watch face service and engine, how to render the watch faces, and what performance issues to pay attention to.


Before we start working on the code, there are a few things that are noteworthy:

First off, there are at least a couple of ways to select your favorite watch faces. On the mobile side, you can do it through the default Android Wear companion app, which provides the preview of all installed watch faces as well as detailed configuration options, if provided, as shown in Figure 1. On the wearable side, doing a long press of the current watch face will prompt you a list of scrollable watch faces with optional simple settings, as in Figure 2.

Secondly, make sure you have the minimum API versions installed for your development. When you create a new project in Android Studio, you should select both form factors in “Phone and Tablet” and “Wearable”. The former requires a minimum of API 18 while the latter requires API 21. If you don’t have these minimum requirements, you should update your APIs through Android Studio’s “Tools -> Android -> SDK Manager”.

Thirdly, make sure both form factors have the following permissions in the manifest file, AndroidManifest.xml.

<uses-permission android_name=
   "" />
<uses-permission android_name=
   "android.permission.WAKE_LOCK" />

The credits for all the amazing background photos should go to my Australian friend, Chris Blunt. He is kind enough to give me permission to use them. If you’re interested, he has many more in the reference link, illustrating the magnificent Australian scenery.

Figure 1: Watch Faces on Mobile Companion App

Figure 2: Watch Faces on Wearable Device

Implementing the Watch Face Service

Watch faces are available through the Android Wear companion app on the mobile device and selector on your wearables. They are implemented as services and their methods are initiated when a watch face is active. If you have done Android Live Wallpaper or Widget programming, creating a service for watch faces should look very familiar to you at first glance.

The service needs to be registered; that can be done inside the AndroidManifest.xml “application” element. This is more like a template, so you can just modify the service name next time around. As an example, “CLiu Digital” is our watch face name with the service titled WatchFaceCLiuDigitalService.

   android_label="CLiu Digital"
      "android.permission.BIND_WALLPAPER" >
      android_resource="@xml/watch_face" />
         "@drawable/preview_cliudigital" />
         "@drawable/preview_cliudigital_circular" />

      <action android_name=
         "android.service.wallpaper.WallpaperService" />
      <category android_name=
         category.WATCH_FACE" />

And the main service code extends the CanvasWatchFaceService and CanvasWatchFaceService.Engine classes. Again, the following code can be treated like a template and your real job is to override the callback methods in the CanvasWatchFaceService.Engine class and fill in your own modifications. Our example sets the update interval to one second (1000 milliseconds), as in UPDATE_INTERVAL, when the watch face is active.

public class WatchFaceCLiuDigitalService
      extends CanvasWatchFaceService {
   private static final long UPDATE_INTERVAL =

   public Engine onCreateEngine() {
      return new Engine();

   private class Engine extends
         CanvasWatchFaceService.Engine {
      Paint mDigitalPaint;
      Paint mDigitalPaintOuter;
      boolean mMute;
      Time mTime;

      static final int MESSAGE_ID_UPDATE_TIME = 1000;
      final Handler mUpdateTimeHandler = new Handler() {
         public void handleMessage(Message message) {
            switch (message.what) {
               case MESSAGE_ID_UPDATE_TIME:
                  if (isVisible() && !isInAmbientMode()) {
                     long delay = UPDATE_INTERVAL
                        - (System.currentTimeMillis()
                           % UPDATE_INTERVAL);
                        (MESSAGE_ID_UPDATE_TIME, delay);

      final BroadcastReceiver mTimeZoneReceiver =
            new BroadcastReceiver() {
         public void onReceive(Context context,
               Intent intent) {
      boolean mRegisteredTimeZoneReceiver = false;

      boolean mLowBitAmbient = false;


      public void onCreate(SurfaceHolder holder) {

         setWatchFaceStyle(new WatchFaceStyle.Builder


         mTime = new Time();

      public void onDestroy() {

      public void onPropertiesChanged(Bundle properties) {
         mLowBitAmbient = properties.getBoolean
            (PROPERTY_LOW_BIT_AMBIENT, false);

      public void onTimeTick() {

      public void onAmbientModeChanged(boolean inAmbientMode) {

         if (mLowBitAmbient) {

      public void onDraw(Canvas canvas, Rect bounds) {

      public void onVisibilityChanged(boolean visible) {

         if (visible) {
         } else


      private void registerReceiver() {
         if (mRegisteredTimeZoneReceiver)

         mRegisteredTimeZoneReceiver = true;
         IntentFilter filter = new
            .registerReceiver(mTimeZoneReceiver, filter);

      private void unregisterReceiver() {
         if (!mRegisteredTimeZoneReceiver)

         mRegisteredTimeZoneReceiver = false;

      private void updateTimer() {

         if (isVisible() && !isInAmbientMode())

Rendering a Simple Watch Face with Digits and a Background

Now, the watch face service is ready, so we can start drawing the face. As a first example, we are going to just display the digital time and calendar in outlined text on top of one of the three randomly selected photos by Chris. We basically store the photos as drawable resource IDs and use Bitmap.createScaledBitmap and BitmapFactory.decodeResource to obtain their original bitmaps. One key to note is inside the overridden onDraw method; Bitmap.createScaledBitmap only scales the image once and only when the image dimensions do not match with those of the drawing canvas. This is trying to avoid overdoing the expensive bitmap scaling operation that will have an impact on the performance and battery life. The results are illustrated in Figure 3, Figure 4, and Figure 5.

final int [] mBackgroundIDs = {R.drawable.cb1,
   R.drawable.cb2, R.drawable.cb3};
Bitmap mBG;

public void onCreate(SurfaceHolder holder) {

   // randomly pick one of the three photos by Chris Blunt
   mBG = Bitmap.createScaledBitmap(BitmapFactory
      mBackgroundIDs[(int)(mBackgroundIDs.length *
         Math.random())]), 320, 320, false);

   mDigitalPaint = new Paint();
   mDigitalPaint.setARGB(255, 255, 255, 255);

   mDigitalPaintOuter = new Paint();
   mDigitalPaintOuter.setARGB(255, 0, 0, 0);


public void onDraw(Canvas canvas, Rect bounds) {

   // draw the background image
   if (mBG == null || mBG.getWidth() != bounds.width()
         || mBG.getHeight() != bounds.height())
      mBG = Bitmap.createScaledBitmap(mBG, bounds.width(),
         bounds.height(), false);
   canvas.drawBitmap(mBG, 0, 0, null);

   // draw the time
   String ts1 = String.format("%02d:%02d:%02d %s",
        mTime.hour % 12, mTime.minute, 
        mTime.second, (mTime.hour < 12) ? "am" : "pm");
   float tw1 = mDigitalPaint.measureText(ts1);
   float tx1 = (bounds.width() - tw1) / 2 + 50;
   float ty1 = bounds.height() / 2 - 80;
   canvas.drawText(ts1, tx1 - 1, ty1 - 1, mDigitalPaintOuter);
   canvas.drawText(ts1, tx1 + 1, ty1 - 1, mDigitalPaintOuter);
   canvas.drawText(ts1, tx1 - 1, ty1 + 1, mDigitalPaintOuter);
   canvas.drawText(ts1, tx1 + 1, ty1 + 1, mDigitalPaintOuter);
   canvas.drawText(ts1, tx1, ty1, mDigitalPaint);

   // draw the date
   String ts2 = String.format("%02d/%02d/%04d",
      mTime.month + 1, mTime.monthDay, mTime.year);
   float tw2 = mDigitalPaint.measureText(ts2);
   float tx2 = (bounds.width() - tw2) / 2 + 50;
   float ty2 = bounds.height() / 2 - 50;
   canvas.drawText(ts2, tx2 - 1, ty2 - 1, mDigitalPaintOuter);
   canvas.drawText(ts2, tx2 + 1, ty2 - 1, mDigitalPaintOuter);
   canvas.drawText(ts2, tx2 - 1, ty2 + 1, mDigitalPaintOuter);
   canvas.drawText(ts2, tx2 + 1, ty2 + 1, mDigitalPaintOuter);
   canvas.drawText(ts2, tx2, ty2, mDigitalPaint);

Figure 3: Watch Face – CLiu Digital 1

Figure 4: Watch Face – CLiu Digital 2

Figure 5: Watch Face – CLiu Digital 3

Getting Creative with Various Watch Faces

As mentioned in the previous sections, most of the service codes are template-like and will not change much except for the onDraw method in CanvasWatchFaceService.Engine. And, of course, this is the part that actually shows the unique differences of your own watch faces. It is where you can get more creative in your designs. Android provides several handy classes you can use to generate algorithmic patterns. For example, inside, there are gradient generators in RadialGradient, LinearGradient, SweepGradient, and so forth. One example we demonstrate here is to use the RadialGradient class to render the watch face background instead of using the photos. This time, we draw the tick marks and all the hands for hours, minutes, and seconds. Note we do not draw the hand for seconds when the watch is in ambient mode, for energy-saving reasons. The result is shown in Figure 6.

public void onDraw(Canvas canvas, Rect bounds) {

   int w = bounds.width(), h = bounds.height();
   float cx = w / 2.0f, cy = h / 2.0f;

   // draw background pattern
   if (mGradient == null) {
      mGradient = new RadialGradient(cx, cy, cx,
         0xff500000, 0xff000050,;
   canvas.drawRect(0, 0, w, h, mBackgroundPaint);

   double sinVal = 0, cosVal = 0, angle = 0;
   float length1 = 0, length2 = 0;
   float x1 = 0, y1 = 0, x2 = 0, y2 = 0;

   // draw ticks
   length1 = cx - 25;
   length2 = cx;
   for (int i = 0; i < 60; i++) {
      angle = (i * Math.PI * 2 / 60);
      sinVal = Math.sin(angle);
      cosVal = Math.cos(angle);
      float len = (i % 5 == 0) ? length1 :
         (length1 + 15);
      x1 = (float)(sinVal * len);
      y1 = (float)(-cosVal * len);
      x2 = (float)(sinVal * length2);
      y2 = (float)(-cosVal * length2);
         canvas.drawLine(cx + x1, cy + y1, cx + x2,
            cy + y2, mTickPaint);

   // draw hours
   length1 = cx - 100;
   angle = ((mTime.hour + (mTime.minute / 60f)) / 6f )
      * (float) Math.PI;
   x1 = (float)(Math.sin(angle) * length1);
   y1 = (float)(-Math.cos(angle) * length1);
   canvas.drawLine(cx, cy, cx + x1, cy + y1, mHourPaint);

   // draw minutes
   length1 = cx - 70;
   angle = mTime.minute / 30f * (float) Math.PI;
   x1 = (float)(Math.sin(angle) * length1);
   y1 = (float)(-Math.cos(angle) * length1);
   canvas.drawLine(cx, cy, cx + x1, cy + y1, mMinutePaint);

   // draw seconds
   if (!isInAmbientMode()) {
      length1 = cx - 40;
      angle = mTime.second / 30f * (float) Math.PI;
      x1 = (float) Math.sin(angle) * length1;
      y1 = (float) -Math.cos(angle) * length1;
      canvas.drawLine(cx, cy, cx + x1, cy + y1, mSecondPaint);

Figure 6: Watch Face – CLiu Analog

Considering Battery Life Saving and Performance

Almost all of the current smart watches have the short-battery-life issue. And, because of the extremely limited resources available on the watches, performance optimization and battery saving should always be on developer’s mind whenever possible. We handle these a little bit in the examples; for example, bitmap scaling, ambient mode, and so on.

Expensive operations dealing with bitmap scaling and loading, animations, anti-aliasing, and constant info updates must be paid very careful attention to. If they can be prepared in advance, try to perform just once or when in great need. When something is unnecessary when not in interactive mode, make sure it is stopped or put into sleep mode. Google’s Android Wear developer page has several recommendations you should study as guidelines.


Watch faces are the most popular and thus the important feature for users. Certainly, for smart watches, displaying the current time only uses the very basic functionality. A variety of info or data can also be shown on the watch face. In the next tutorial, we can learn to provide users some configuration options in an interactive manner so that they can set up their preferences as what they want to see on their watches. As developers, we also need to know how to retrieve data in a reasonably efficient manner so that overall performance and battery life are only affected to a minimum.


  1. Download example source code from the link at the end of this article
  2. Chris Blunt Photography at:
  3. Android Developers at:
  4. Androidlet at

About the Author

Chunyen Liu has been a software professional in Taiwan and the United States. He is a published author of 35+ articles and 100+ tiny apps, software patentee, technical reviewer, and programming contest winner by ACM/IBM/SUN. He holds advanced Computer Science degrees and trained in 20+ graduate-level courses. On the non-technical side, he is a certified coach, certified umpire, and rated player of USA Table Tennis, having won categorized events at State Championships and the US Open.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories