Home > UI Handling > Developing Android Home Screen Widgets

Developing Android Home Screen Widgets

In this article, I’ll be showing you how to create a widget for the Android homescreen. Widgets can be useful to provide condensed information, without necessarily having to open your application. In addition to showing information, a widget can also be used to trigger certain actions related to your application. Instead of forcing the user to open up your application and navigating to a certain screen to perform an action, a widget can provide the user with a quick shortcut to that action. You can also dynamically change the layout of your widget (ex: when the user presses a button, the button can be highlighted, or a piece of text can altered). This article is accompanied by a sample android application that includes the widgets we’ll be discussing here.

Designing the widget

An Android Widget can have different sizes. As can be seen in the screenshot below, the homescreen is divided into several squares, where each widget fits graphically with other widgets and with the other elements (like application shortcuts) of the Android Home screen.

The home screen contains 16 such squares (4×4), and has a width of 320dp and a height of 400dp.

Supported Widget sizes

Depending on the screen orientation, your widget will have different dimensions. What does remain consistent is the number of squares the widget will occupy.
There are six standard widget sizes. Three sizes related to the Home screen grid of 4 x 4 (portrait), the other 3 relate to the Home screen grid of 4 x 4 (landscape) cells.
These dimensions correspond to the bounding boxes for the widget we’ll see in the next section.

portrait

4 x 1 squares, dimension 320 x 100
3 x 3 squares, dimension 240 x 300
2 x 2 squares, dimension 160 x 200

landscape

4 x 1 squares, dimension 424 x 74
3 x 3 squares, dimension 318 x 222
2 x 2 squares, dimension 212 x 148

In Android terminology, a Widget is made up of the following components

  • A bounding box
  • A frame
  • The widget’s graphical controls and other elements.

The bounding box

We’ll start by creating an image canvas for a 4×1 widget. This needs to be 320×100.

The bounding box is the full canvas you have at your disposal. We won’t be occupying all of that space, as we need to take into account the padding. Start by defining the frame that will form the boundaries of your image:

The frame

We need to ensure that sufficient padding (empty space) is available around the widget, as we don’t want it to take up too much space when placed alongside other widgets. As such, our Widget should be contained within a frame that we’ll define inside the bounding box, leaving some space on the top, bottom, left and right hand side of the bounding box.

The actual content of the widgets shouldn’t draw to the edge of these dimensions, but fit inside a frame withing the bounding box, as described above.

We draw a rectangle inside the frame, and contract the selection to round the edges a bit.

In order to have a consistent look & feel, you should apply the following blending options to your image:

The widget’s graphical controls and other elements.

As we’re going to add icons and text to our widget by putting Android Views on a Layout file, these shouldn’t be included in our final image. However, to get a visual idea on how the widget will look like, it might be interesting to put these controls onto your image. In Photoshop, just put them in seperate layers that you can exclude before saving the final PNG file.

Your image should look something like this:

Save your widget artwork (without the controls) using the appropriate bounding box size in PNG-24 format on a transparent background and in 8-bit color.

Your image should look something like this:

Putting the widget on the screen.

Now that we’ve got the graphics out of the way, it’s time to start coding…
A Widget is basically a broadcast receiver, capable of listening for specific intents. One intent that the Widget is particulary interested in is the android.appwidget.action.APPWIDGET_UPDATE intent.

Now that we have designed our widget, we can add it to our application. In order to do that we need to execute the following steps:

Define the widget in the manifest

We start by defining our widget like this in the application Manifest.

        <receiver android:name=".SampleWidgetProvider41_1" android:label="@string/app_widget_4_1">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_provider_4_1" />
        </receiver>

The definition shows us the following :

  • the widget is a broadcast receiver
  • the widget has an intent-filter, meaning it will listen for certain events
  • the widget that can be given a label. The label is shown in the list of widgets when the user wants to add a widget to the homescreen.
  • meta data is provided, providing a pointer to an XML resource, used to further define our widget.
  • We’ll provide a label in our strings.xml that will be used in the list of widgets.

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="hello">Hello World, AndroidWidgetSample!</string>
        <string name="app_name">AndroidWidgetSample</string>
        <string name="app_widget_4_1">Our Widget 4x1</string>
    </resources>
    

    Appwidget proviuder xml definition

    Next we need to create an appwidget provider definition. We specify a minimum height and width for the widget, as well as an update interval, and a pointer to our initial layout.

    <?xml version="1.0" encoding="utf-8"?>
    <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
        android:minWidth="72dip"
        android:minHeight="72dip"
        android:updatePeriodMillis="86400000"
        android:initialLayout="@layout/latify_widget_1_1_100_100"
    />
    

    For calculating the minWidth and minHeight, we need to some math.
    Because the Home screen’s layout orientation (and thus, the cell sizes) can change, as a rule of thumb, you should assume the worst-case cell size of 74 pixels for the height and width of a cell
    So, for a 1×1 widget, by applying the formula (number of cells * 74) – 2, we arrive at a minWidth=72dip and minHeight=72dip.

    The android:updatePeriodMillis defines the interval when the widget onUpdate method will be called. There is a minimum for this (so we cannot set this to whatever we want to have frequent updates on the widget).

    It is documented somewhere but I wasn’t able to find it. In order to have more frequent updates on the Widget, you’ll need to implement an AlarmManager to send events to the Widget.

    The initial layout is the actual layout file that our widget will use, leading us to the next section.

    Defining the layout

    The layout file is basically the same layout structure as you would use for a normal activity, however, you need to take certain space constraints into account. Android support different sizes when it comes to home screen widgets.

    We’ll start with a very simple layout, that just uses our widget as a background image

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/widget"
        android:layout_width="320dp"
        android:layout_height="100dp"
        android:focusable="true"
        android:background="@drawable/widget4_1">
    </RelativeLayout>
    

    Notice how we also set the width and height to 320dp x 100dp as we’re dealing with a 4×1 widget.

    We’ll now add our icon and some text to the widget. Here’s the complete layout of the widget:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/widget"
        android:layout_width="320dp"
        android:layout_height="100dp"
        android:focusable="true"
        style="@style/WidgetBackground_4_1">
    
            <ImageView
            android:id="@+id/icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="15dip"
            android:layout_marginLeft="15dip"
            android:layout_marginRight="10dip"
            android:layout_marginBottom="0dip"
            android:src="@drawable/icon" />
    
        <TextView
            android:id="@+id/word_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/icon"
            android:layout_marginTop="25dip"
            android:layout_marginLeft="0dip"
            android:layout_marginRight="10dip"
            android:layout_marginBottom="0dip"
    
            android:text="Our Widget Title"
            style="@style/WidgetTitle" />      
    
        <TextView
            android:id="@+id/word_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/word_title"
            android:layout_toRightOf="@id/icon"
            android:layout_marginTop="5dip"
            android:layout_marginLeft="0dip"
            android:layout_marginRight="10dip"
            android:layout_marginBottom="0dip"
            android:text="Our Widget Text comes here."
            style="@style/WidgetText" />           
    
    </RelativeLayout>
    

    You can use the Eclipse Graphical Layout to determine the correct margins, and get an idea on how the widget will look like.

    Also notice how we’ve applied a style for the layout. We’ll put stuff like background images, fonts into a styles.xml. The complete style definition for the widget looks like this :

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
    	<style name="WidgetBackground_4_1">
            <item name="android:background">@drawable/widget_4_1</item>
        </style>
    
    	<style name="WidgetTitle">
            <item name="android:textSize">18sp</item>
            <item name="android:textStyle">bold</item>
            <item name="android:textColor">@android:color/white</item>
        </style>
    
        <style name="WidgetText">
            <item name="android:textSize">12sp</item>
            <item name="android:textColor">@android:color/white</item>
        </style>
    
    </resources>
    

    The AppWidgetProvider

    As with any element we define in the manifest (activities,services,broadcastreceivers), the actual implementation of the widget can be found in a java class, extending AppWidgetProvider.
    For now, create the SampleWidgetProvider4_1 like this:

    public class SampleWidgetProvider4_1 extends AppWidgetProvider {
    
    	@Override
    	public void onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds) {
    		super.onUpdate(context, appWidgetManager, appWidgetIds);
    		// implementation will follow
    	}
    }
    

    Project structure

    Your project structure should look like this :

    To summarize so far, the project should contain :

    • Widget defined in manifest (receiver element)
    • Widget provider xml definition in xml/widget_provider_4_1.xml
    • Widget layout file in layout/widget_layout_4_1.xml
    • Widget layout styles in values/styles.xml
    • Widget labels in values/strings.xml
    • Widget image files (backgrounds & controls) in drawable
    • Widget AppWidgetProvider class in your source folder

    With these tasks out of the way, we’re able to display our Widget on the homescreen. Nothing else going on at the moment, so it’s time to start adding some widget behavior.
    The sample application contains 2 widgets (a 4×1 widget and a 2×2 widget). This is how they look like:

    As you can see, they fit nicely on the home screen alongside the other icons.

    Widget Behavior

    Obviously, displaying a widget is one thing, making it act upon certain actions is another thing.

    Clicking on the widget

    The first thing we’ll do is make our Widget aware of the fact that the user has clicked on it. What typically happens in this type of scenario is that the main application is started.

    In order to do that, we need to put the following code in our Widget onUpdate method:

              // When we click the widget, we want to open our main activity.
              Intent launchActivity = new Intent(context,AndroidWidgetSample.class);
              PendingIntent pendingIntent = PendingIntent.getActivity(context,0, launchActivity, 0);
              remoteViews.setOnClickPendingIntent(R.id.widget, pendingIntent2);
    
              ComponentName thisWidget = new ComponentName(context, SampleWidgetProvider4_1.class);
              AppWidgetManager manager = AppWidgetManager.getInstance(context);
              manager.updateAppWidget(thisWidget, remoteViews);
    

    As you can see, we set an OnClickPendingIntent on our widget. The Pending Intent will launch an Activity, in our case, the only activity in our sample, being the AndroidWidgetSample activity.
    The setOnClickPendingIntent is targeted to our entire widget (R.id.widget), and not to a specific control. This means that if we click on an area in our widget that doesn’t cover any other clickable controls, this activity will be launched. Note also that we need to call the updateAppWidget on the AppWidgetManager in order for it to respond to the onClick.

    Clicking on a specific control on the widget

    An activity can also contain certain controls that the user may want to manipulate. A widget might contain a play/stop button, or another component that you would like to specifically target. In our sample, we only have an icon and a piece of text, but we’ll make sure that a specific action is being triggered when clicking on the icon.

    When the icon is clicked on the widget, we broadcast an intent containing the ACTION_WIDGET_UPDATE_FROM_WIDGET action.

    		  Intent intent = new Intent(context, SampleWidgetProvider4_1.class);
    		  intent.setAction(Constants.ACTION_WIDGET_UPDATE_FROM_WIDGET);
    		  intent.putExtra(Constants.INTENT_EXTRA_WIDGET_TEXT,"Icon clicked on Widget");
    
              PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
              remoteViews.setOnClickPendingIntent(R.id.icon,actionPendingIntent);
    

    Similar code to the one we saw before. Note here that the setOnClickPendingIntent is targeting the icon on our widget, so this intent will only be broadcast if we hit the icon on the widget.

    In the manifest, the widget is configured with an intent filter to match this action. By doing this, we can intercept this intent in the onReceive method of the Widget (remember that the AppWidgetProvider is nothing more than a fancy BroadcastReceiver.

            <receiver android:name=".SampleWidgetProvider4_1" android:label="@string/app_widget_4_1">
                <intent-filter>
                    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                    <action android:name="ACTION.WIDGET.UPDATE.FROM.ACTIVITY"/>
                    <action android:name="ACTION_WIDGET_UPDATE_FROM_ALARM"/>
                    <action android:name="ACTION_WIDGET_UPDATE_FROM_WIDGET"/>
                </intent-filter>
                <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_provider_4_1" />
            </receiver>
    

    Note how I added some other actions as well, as this allows us to differentiate between different messages being sent to our widget.

    Allowing Activities to update the Widget

    In order to update the Widget from our main activity, we’ve placed a button on the main layout, that does the following:

    		btnSendText = (Button) findViewById(R.id.btn_send_text);
    
            btnSendText.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                	sendTextToWidget(AndroidWidgetSample.this);
                }
            });
    
        private void sendTextToWidget(Context context) {
    		Intent uiIntent = new Intent(Constants.ACTION_WIDGET_UPDATE_FROM_ACTIVITY);
    		SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
    		uiIntent.putExtra(Constants.INTENT_EXTRA_WIDGET_TEXT,"Button clicked on Activity at " + sdf.format(new Date()));
    		context.sendBroadcast(uiIntent2);
        }
    

    As you can see, the button does nothing more that broadcast an intent that will be picked up by our Widget. (same thing as with the icon click in the widget itself).

    The Widget onReceive method

    All these actions are processed by the Widget through its onReceive method. Here, we can get a hold of the remoteView, and in our case, update the TextView using the setTextViewText method on the Widget. Keep in mind that a Widgets layout is not accessed in the same way as an Activities layout. We cannot execute a findViewById to get a reference to TextViews or Buttons. We always need to pass by the removeView.

    	@Override
    	public void onReceive(Context context, Intent intent) {
    		Log.i(Constants.TAG, "onReceive called with " + intent.getAction());
    		RemoteViews remoteViews = new RemoteViews(context.getPackageName (), R.layout.widget_layout_4_1);
    		if (intent.getAction().equals(Constants.ACTION_WIDGET_UPDATE_FROM_ACTIVITY)) {
    			String widgetText = intent.getExtras().getString(Constants.INTENT_EXTRA_WIDGET_TEXT);
    			remoteViews.setTextViewText(R.id.word_text, widgetText);
            } else if (intent.getAction().equals(Constants.ACTION_WIDGET_UPDATE_FROM_ALARM)) {
            	String widgetText = intent.getExtras().getString(Constants.INTENT_EXTRA_WIDGET_TEXT);
            	SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
    			remoteViews.setTextViewText(R.id.word_text, widgetText + " at " + sdf.format(new Date()) );
            } else if (intent.getAction().equals(Constants.ACTION_WIDGET_UPDATE_FROM_WIDGET)) {
    			String widgetText = null;
    			if (intent.getExtras()!= null) {
    				widgetText = intent.getExtras().getString(Constants.INTENT_EXTRA_WIDGET_TEXT);
    			} else {
    				widgetText = "Extras was null";
    			}
            	SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
    			remoteViews.setTextViewText(R.id.word_text, widgetText + " at " + sdf.format(new Date()) );
            } else {
            	super.onReceive(context, intent);
            }
    		ComponentName cn = new ComponentName(context, SampleWidgetProvider4_1.class);
    		AppWidgetManager.getInstance(context).updateAppWidget(cn, remoteViews);
    
    	}
    

    If we want to highlight the icon when it has been clicked for example, we use the following code

    remoteViews.setImageViewResource(R.id.icon, R.drawable.icon_red);
    

    This will simply put another drawable on the icon layout element, allowing for an image toggle effect.

    Conclusions

    Widgets can provide added value to your application by giving the user certain shortcuts, or giving them the opportunity to see vital information regarding your app by just looking at the home screen. When developing widgets, you do need to take into account certain constraints. You’ll need to adhere to certain size requirements, and you also need to be aware of the fact that Widgets are not the same as activities. The way you interact with them from a development perspective is different than what you’re used to when working with activities.

    References

  1. Chris Cooper
    June 9th, 2011 at 23:58 | #1

    Thank you so much for this tutorial, been struggling to get intent launching widgets for hours and it’s right here!!! Wish I’d found this much earlier, it would have saved me a lot of time! Keep up the good work :D

  2. Uma Sun
    June 13th, 2011 at 15:42 | #2

    What tools was used to develop the images?

  3. Mike Wolfson
    June 13th, 2011 at 20:53 | #3

    Great article. I was also struggling with launching an Intent from a Widget, and this was by far the best resource I found describing the process. Thanks for posting this – it helped me a lot.

  4. admin
    June 14th, 2011 at 09:23 | #4

    @Uma Sun
    Photoshop was used, but any decent image editor should do.

  5. June 27th, 2011 at 22:48 | #5

    Great post! Made my first widget now :)

  6. Chintan
    July 3rd, 2011 at 11:38 | #6

    Hi,

    i have just started programming on android.
    i am trying to make 4*3 home widget for video play.
    i want to play my video inside the widget at home screen.

    please help me.
    i need a code example for video play widget.

  7. Christopher
    September 14th, 2011 at 16:33 | #7

    For a widget background that simple, you do not need to draw a custom image at all.
    Use Android’s Drawable resources, in particular a ShapeDrawable, define in XML.

    Your entire background could be defined with , and within a tag.

  8. Christopher
    September 14th, 2011 at 16:33 | #8

    For a widget background that simple, you do not need to draw a custom image at all.
    Use Android’s Drawable resources, in particular a ShapeDrawable, define in XML.

    Your entire background could be defined with [stroke], [corners] and [gradient] within a [shape] tag.

  9. Shaun Aga
    September 23rd, 2011 at 00:42 | #9

    Great example. Thanks!

    Do you have any idea how to fit images better? When I replace the 72×72 icon.png file with an arbitrary image (say 512×512) I’m having a problem with the image occupying the whole text box. I have tried to set a maxHeight & maxWidth for the image to 72 but it still doesn’t work.

    Or put the question another way,does the image have to be modified via photoshop to 72 x 72?

    Thanks.

  10. GBH
    November 27th, 2011 at 12:33 | #10

    Hi, great post, it was very helpful !
    i’m trying to send an image to the widget (from a gallery in the main activity), i couldn’t figure out a way, either sending the object or just the id !!
    thanx for answering !

  11. Gabe
    December 13th, 2011 at 19:57 | #11

    Thanks for the example. There is one small error error in your manifest on github and in the part you show here (but that is fixed in the manifest): The intent filter for the 2×2 widget filters for ACTION.WIDGET.UPDATE.FROM.ACTIVITY, which should be ACTION_WIDGET_UPDATE_FROM_ACTIVITY.

  12. Hardik
    December 26th, 2011 at 05:51 | #12

    Nice post !!..keep up the good work..!! Thanks

  13. devendra
    December 28th, 2011 at 09:53 | #13

    MY question is that when i open the emulator the google and music widget is

    by default already on home screen…so i also want that same thing for my

    widget that when i open my emulator i can see my widget like google and

    music…
    plzzz help me and reply…
    i searched every where but i found nothing…

  14. January 13th, 2012 at 21:31 | #14

    Great tutorial, Thanks

  15. Mustafa
    January 16th, 2012 at 07:25 | #15

    is there an Example how to make the text scroll horizontally for the home screen widget?

  16. xuelvming
    February 28th, 2012 at 01:56 | #16

    @Chintan
    Sorry to tell you that.Widget palying a video is never supported.As you see,widget only support some simple view .e.g TextView. So i am afraid you need to try some other solution.

  17. Lefinator
    March 24th, 2012 at 22:45 | #17

    I just wanted to thank you for taking your time to write this tutorial. It has been of great assistance to me while learning to design Android App Widgets.

  18. wizard
    May 15th, 2012 at 04:28 | #18

    I do not understand where the magic numbers “320dp x 100dp”?
    How to determine the actual size of the widget?

  19. Rajesh
    April 2nd, 2013 at 08:45 | #19

    Thank you so much Mr. developer, i had use this tutorial to implement my app.

  1. No trackbacks yet.