Professional Documents
Culture Documents
Lars Vogel
Version 2.6
12.01.2013
Revision History
Lars
Revision 0.1 01.09.2010 Created
Vogel
Lars
Revision 0.2 - 2.6 13.12.2010 - 12.01.2013 bug fixes and enhancements
Vogel
This article describes how to create widgets in Android. It is based on Eclipse 4.2, Java 1.6 and
Android 4.2.
Table of Contents
1. Prerequisites
2. Android Widgets
2.1. Overview about AppWidgets
2.2. Steps to create a Widget
2.3. Widget size
3. Creating the Broadcast receiver for the widget
3.1. Create and configure widget
3.2. Available views and layouts
3.3. AppWidgetProvider
4. Widget updates
5. Tutorial: Widget with fixed update interval
6. Collection View Widgets
7. Enabling a Widget for the Lock Screen
8. Tutorial: Update widget via a service
9. Tutorial: Add widget to lockscreen
10. Thank you
11. Questions and Discussion
12. Links and Literature
12.1. Source Code
12.2. Android Widget Resources
12.3. Android Resources
12.4. vogella Resources
1. Prerequisites
The following description assume that you have already experience in building standard Android
application. Please see Android Tutorial . It also uses partly Android services. You find an
introduction into Android Services in Android Service Tutorial .
2. Android Widgets
2.1. Overview about AppWidgets
Widgets are little applications which can be placed on a widget host, typically the homescreen or
the lockscreen, of your Android device.
A Widget runs as part of the process of its host. This requires that Widgets preserve the
permissions of their application.
Widgets use RemoteViews to create there user interface. A RemoteView can be executed by
another process with the same permissions as the original application. This way the Widget runs
with the permissions of its defining application.
A Widget will take a certain amount of cells on the homescreen. A cell is usually used to display
the icon of one application. As a calculation rule you should define the size of the widget with
the formula: ((Number of columns / rows)* 74) - 2. These are device independent pixels and the
-2 is used to avoid rounding issues.
As of Android 3.1 a Widgets can be flexible in size, e.g. the user can make it larger or smaller.
To enable this for Widgets you can use the android:resizeMode="horizontal|vertical"
attribute in the XML configuration file for the widget.
To register a widget you create a BroadcastReceiver with an intent filter for the
android.appwidget.action.APPWIDGET_UPDATE action .
<receiver
android:icon="@drawable/icon"
android:label="Example Widget"
android:name="MyWidgetProvider" >
<intent-filter >
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"
/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_info" />
</receiver>
The BroadcastReceiver can get an label and icon assigned. These are used in the list of
available Widgets.
You also specify the meta-data for the widget via the
android:name="android.appwidget.provider attribute. The configuration file referred by
this meta-data contains the configuration settings for the widget. If contains for example the
update interface, the size and the initial layout of the widget.
</appwidget-provider>
A widget is restricted in the View classes it can use. As layouts you can use the FrameLayout,
LinearLayout and RelativeLayout classes. As views you can use AnalogClock, Button,
Chromometer, ImageButton, ImageView, ProgressBar and TextView.
As of Android 3.0 more views are available: GridView, ListView, StackView, ViewFlipper
and AdapterViewFlipper. This adapter Views require that you define a collection view widget
which is described later in this tutorial.
The only interaction that is possible on the Views of a Widget is via on OnClickListener. This
OnClickListener can be registered on a widget and is triggered by the user.
3.3. AppWidgetProvider
The AppWidgetProvider class implements the onReceive() method, extracts the required
information and calls the following widget lifecycle methods.
As you can add several instances of a widget to the homescreen you have lifecycle methods
which are called only for the first instance added / removed to the homescreen and others which
are called for every instance of your widget.
Method Description
onEnabled() Called the first time an instance of your widget is added to the homescreen
onDisabled() Called once the last instance of your widget is removed from the homescreen.
Called for every update of the widget. Contains the ids of appWidgetIds for which
an update is needed. Note that this may be all of the AppWidget instances for this
onUpdate() provider, or just a subset of them, as stated in the methods JavaDoc. For example if
more than one widget is added to the homescreen, only the last one changes (until
reinstall).
onDeleted() Widget instance is removed from the homescreen
All long running operations in these methods should be performed in a service, as the execution
time for a broadcast receiver is limited. Using asynchronous processing in the onReceive()
method does not help as the system can kill the broadcast process after his onReceive() method.
4. Widget updates
A Widget gets its data on a periodic timetable. There are two methods to update a widget, one is
based on an XML configuration file and the other is based on the Android AlarmManager
service.
In the widget configuration file you can specify a fixed update interval. The system will wake up
after this time interval and call your broadcast receiver to update the widget. The smallest update
interval is 1800000 milliseconds (30 minutes).
The AlarmManager allows you to be more resource efficient and to have a higher frequency of
updates. To use this approach you define a service and schedule this service via the
AlarmManager regularly. This service updates the widget.
Please note that a higher update frequency will wake up the phone from the energy safe mode.
As a result your widget consumes more energy.
<stroke
android:width="2dp"
android:color="#FFFFFFFF" />
<gradient
android:angle="225"
android:endColor="#DD2ECCFA"
android:startColor="#DD000000" />
<corners
android:bottomLeftRadius="7dp"
android:bottomRightRadius="7dp"
android:topLeftRadius="7dp"
android:topRightRadius="7dp" />
</shape>
<TextView
android:id="@+id/update"
style="@android:style/TextAppearance.Medium"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center_horizontal|center_vertical"
android:layout_margin="4dip"
android:text="Static Text" >
</TextView>
</LinearLayout>
Create the AppWidgetProvider metadata widget_info.xml file, via File → New → Android
→ Android XML File
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/widget_layout"
android:minHeight="72dp"
android:minWidth="300dp"
android:updatePeriodMillis="300000" >
</appwidget-provider>
package de.vogella.android.widget.example;
import java.util.Random;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.RemoteViews;
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// Register an onClickListener
Intent intent = new Intent(context, MyWidgetProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
<application
android:icon="@drawable/icon"
android:label="@string/app_name" >
<receiver android:name="MyWidgetProvider" >
<intent-filter >
<action
android:name="android.appwidget.action.APPWIDGET_UPDATE"
/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_info" />
</receiver>
</application>
</manifest>
Run your app. Once your app has been deployed, long press on your desktop and install your
new widget.
6. Collection View Widgets
Collection view Widgets add support for views like List, Stack and Grid.
For Collection view Widgets you need two layouts, one for the widget and one for each item in
the widget collection.
The Widget items are filled by an instance of the RemoteViewsFactory factory class.
This factory class is provided by a service which must extend the RemoteViewsService class.
This service requires the andriod.permission.BIND_REMOTEVIEWS permission.
To connect your Views with the service you use your onUpdate() method in your Widget
receiver. You define an Intent pointing to the service and use the setRemoteAdapter method
on the RemoteViews class.
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:widgetCategory="keyguard|home_screen"
...
>
...
</appwidget-provider>
In this example you declare a widget to support both - the Home and the Lock screens. If you
recompile and launch your application now, you will be able to add the widget to the Lock
Screen already.
You can also detect a widget category at runtime. For this in the
AppWidgetProvider.onUpdate() method you can check for the category option of a widget
with the following code.
int category =
options.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1);
boolean isLockScreen = category ==
AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD;
Using this technique you can decide in runtime whether widgets, your application provides, will
look different, when they are hosted on the Lock Screen.
Similarly to how you used android:initialLayout attribute for defining an initial layout for Home
screen widgets, you can use a new android:initialKeyguardLayout attribute for Lock screen
in the AppWidgetProviderInfo XML file. This layout will appear immediately after a widget is
added and will be replaced by the real layout once widget is initialized.
8. Tutorial: Update widget via a service
A BroadcastReceiver must finish his onReceive() method within 5 secs.
For potentially long running actions, as for example network access, you should do this
processing in a service and perform the update the widgets from the service.
The following will demonstrate the usage of a service to update the widget.
package de.vogella.android.widget.example;
import java.util.Random;
import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import android.widget.RemoteViews;
@Override
public void onStart(Intent intent, int startId) {
Log.i(LOG, "Called");
// Create some random data
// Register an onClickListener
Intent clickIntent = new Intent(this.getApplicationContext(),
MyWidgetProvider.class);
clickIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS,
allWidgetIds);
PendingIntent pendingIntent =
PendingIntent.getBroadcast(getApplicationContext(), 0, clickIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.update, pendingIntent);
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
stopSelf();
super.onStart(intent, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
<service android:name=".UpdateWidgetService"></service>
Change MyWidgetProvider to the following. It will now only construct the service and start it.
package de.vogella.android.widget.example;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
Once called this service will update all widgets. You can click on one of the widgets to update all
widgets.
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:widgetCategory="keyguard|home_screen"
...
>
...
</appwidget-provider>