Вы находитесь на странице: 1из 10

WIDGETS: Los pasos principales para la creación de un widget Android son los siguientes:

1. Definición de su interfaz gráfica (layout).


2. Configuración XML del widget (AppWidgetProviderInfo).
3. Implementación de la funcionalidad del widget (AppWidgetProvider) , especialmente su evento de actualización.
4. Declaración del widget en el Android Manifest de la aplicación.

Paso 1: La interfaz del widget está compuesta por dos FrameLayout, uno negro exterior y uno blanco interior más
pequeño para simular el marco, y una etiqueta de texto (TextView) que muestra el mensaje. Para este ejemplo se llama
“miwidget.xml“:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dip">
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#000000"
android:padding="10dip" >
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FFFFFF"
android:padding="5dip" >
<TextView android:id="@+id/txtMensaje"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textColor="#000000"
android:text="Mi Primer Widget" />
</FrameLayout>
</FrameLayout>
</LinearLayout>

Paso 2: Crear un nuevo XML donde definir la frecuencia de actualización. Este XML se crea en la carpeta \res\xml del
proyecto y que se llama “miwidget_wprovider.xml”:
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/miwidget"
android:minWidth="146dip"
android:minHeight="72dip"
android:label="Mi Primer Widget"
android:updatePeriodMillis="3600000"
/>

Para el widget se definen las siguientes propiedades:


initialLayout: referencia al layout XML que hemos creado en el paso anterior.
minWidth: ancho mínimo del widget en pantalla, en dp (density-independent pixels).
minHeight: alto mínimo del widget en pantalla, en dp (density-independent pixels).
label: nombre del widget que se mostrará en el menú de selección de Android.
updatePeriodMillis: frecuencia de actualización del widget, en milisegundos.

La pantalla inicial de Android se divide en 4×4 celdas donde se pueden colocar aplicaciones, accesos directos y widgets,
según la orientación de la pantalla. Existe una fórmula sencilla para ajustar las dimensiones del widget para que ocupe
un número determinado de celdas sea cual sea la orientación:
ancho_mínimo = (num_celdas * 74) – 2
alto_mínimo = (num_celdas * 74) – 2

Si se desea que el widget ocupe un tamaño mínimo de 2 celdas de ancho por 1 celda de alto, se deben indicar unas
dimensiones de 146dp x 72dp.
Paso 3: Implantar la funcionalidad del widget en su clase java asociada. Esta clase debe heredar de
AppWidgetProvider, que a su vez no es más que una clase auxiliar derivada de BroadcastReceiver, ya que los
widgets de Android no son más que un caso particular de este tipo de componentes. Se deben implementar los mensajes
a los que vamos a responder desde nuestro widget:
onEnabled(): lanzado cuando se añade al escritorio la primera instancia de un widget.
onUpdate(): lanzado periódicamente cada vez que se debe actualizar un widget.
onDeleted(): lanzado cuando se elimina del escritorio una instancia de un widget.
onDisabled(): lanzado cuando se elimina del escritorio la última instancia de un widget.

Implementar como mínimo el evento onUpdate(). El resto de métodos dependerán de la funcionalidad de nuestro
widget. En nuestro caso particular no nos hará falta ninguno de ellos ya que el widget que estamos creando no contiene
ningún dato actualizable, por lo que crearemos la clase, llamada MiWidget, pero dejaremos vacío por el momento el
método onUpdate(). En el siguiente apartado veremos qué cosas podemos hacer dentro de estos métodos.
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
public class MiWidget extends AppWidgetProvider {
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
//Actualizar el widget
//...
}
}

Paso 4: Declarar el widget dentro del manifest de la aplicación. Editar el AndroidManifest.xml para incluir la siguiente
declaración dentro del elemento <application>:
<application>
...
<receiver android:name=".MiWidget" android:label="Mi Primer Widget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/miwidget_wprovider" />
</receiver>
</application>

El widget se declarará como un elemento <receiver> y se debe ingresar la siguiente información:


 Atributo name: Referencia a la clase java de nuestro widget, creada en el paso anterior.
 Elemento <intent-filter>, donde indicaremos los eventos a los que responderá el widget, normalmente
añadiremos el evento APPWIDGET_UPDATE, para detectar la acción de actualización.
 Elemento <meta-data>, donde haremos referencia con su atributo resource al XML de configuración que
creamos en el segundo paso del proceso.

Para probarlo, ir a la pantalla principal del emulador y añadir nuestro widget al escritorio tal cómo lo haríamos en nuestro
teléfono (digitación larga sobre el escritorio o tecla Menú, seleccionar la opción Widgets, y por último seleccionar nuestro
Widget).
Android Widgets
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
3.4. Receiver and asynchronous processing
4. Widget updates
5. Exercise: widget with fixed update interval
5.1. Target
5.2. Create project and widget implementation
5.3. Validate
6. Collection View Widgets
7. Enabling a Widget for the Lock Screen
8. Exercise: Update widget via a service
9. About this website
9.1. Donate to support free tutorials
9.2. Questions and discussion
9.3. License for this tutorial and its code
10. Links and Literature
10.1. Source Code
10.2. Android Widget Resources
10.3. Android Resources
10.4. vogella Resources

1. Prerequisites
The following description assume that you already have experience in building standard Android application.
Please see Android Tutorial. It also partly uses 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 home screen or the lock
screen of your Android device.
A widget runs as part of the process of its host. This requires that the widget preserves the permissions of their
application.
Widget use RemoteViews to create their 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.
The user interface for a Widget is defined by a broadcast receiver. This receiver inflates its layout into an object
of type RemoteViews . This object is delivered to Android, which hands it over the home screen application.
2.2. Steps to create a Widget
To create a widget, you:
 Define a layout file
 Create an XML file ( AppWidgetProviderInfo ) which describes the properties of the widget, e.g. size or
the fixed update frequency.
 Create a BroadcastReceiver which is used to build the user interface of the widget.
 Enter the Widget configuration in the AndroidManifest.xml file.
 Optional you can specify a configuration activity which is called once a new instance of the widget is
added to the widget host.
2.3. Widget size
Before Android 3.1 a widget always took a fixed amount of cells on the home screen. 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 errors.
As of Android 3.1 a widget can be flexible in size, e.g., the user can make it larger or smaller. To enable this for
widget, you can use the android:resizeMode="horizontal|vertical" attribute in the XML configuration
file for the widget.
3. Creating the Broadcast receiver for the widget
3.1. Create and configure widget
To register a widget, you create a broadcast receiver 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 receiver can get a label and icon assigned. These are used in the list of available widgets in the Android
launcher.
You also specify the meta-data for the widget via
the android:name="android.appwidget.provider attribute. The configuration file referred by this metadata
contains the configuration settings for the widget. It contains, for example, the update interface, the size and the
initial layout of the widget.

<?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="146dp"
android:updatePeriodMillis="1800000" >

</appwidget-provider>

3.2. Available views and layouts


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 . These adapter
views require that you define acollection view widget which is described later in this book.
The only interaction that is possible with the views of a widget is via an OnClickListener event.
This OnClickListener can be registered on a widget and is triggered by the user.
3.3. AppWidgetProvider
Your BroadcastReceiver implementation typically extends the AppWidgetProvider class.
The AppWidgetProvider class implements the onReceive() method, extracts the required information and
calls the following widget life cycle methods.
As you can add several instances of a widget to the home screen, you have life cycle methods which are called
only for the first instance added / removed to the home screen and others which are called for every instance of
your widget.
Table 1. Life cycle method

Method Description

onEnabled() Called the first time an instance of your widget is added to the home screen.

onDisabled() Called once the last instance of your widget is removed from the home screen.

onUpdate() 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 provider, or just a subset of them, as stated in
the method's JavaDoc. For example, if more than one widget is added to the
home screen, only the last one changes (until reinstall).

onDeleted() Widget instance is removed from the home screen.

3.4. Receiver and asynchronous processing


A widget has the same runtime restrictions as a normal broadcast receiver, i.e., it has only 5 seconds to finish its
processing.
A receive (widget) should therefore perform time consuming operations in a service and perform the update of
the widgets from the service.
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.

5. Exercise: widget with fixed update interval


5.1. Target
In the following tutorial you create a widget which displays a random number. This random number is updated
every 30 minutes. You also register an OnClickListener so that the widgets updates once the user clicks on
it.
The resulting widget will look like the following.

5.2. Create project and widget implementation


Create a new Android project called de.vogella.android.widget.example with an activity in the
package de.vogella.android.widget.example .
Create a new file myshape.xml in the /res/drawable folder. This file defines the drawable used as
background in the widget.

<?xml version="1.0" encoding="UTF-8"?>


<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<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>

Define the following widget_layout.xml file under the res/layout folder.

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dip"
android:background="@drawable/myshape" >
<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>

Create the following receiver class which is called during updates.


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;
public class MyWidgetProvider extends AppWidgetProvider {
private static final String ACTION_CLICK = "ACTION_CLICK";
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// Get all ids
ComponentName thisWidget = new ComponentName(context,
MyWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
for (int widgetId : allWidgetIds) {
// create some random data
int number = (new Random().nextInt(100));
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget_layout);
Log.w("WidgetExample", String.valueOf(number));
// Set the text
remoteViews.setTextViewText(R.id.update, String.valueOf(number));
// Register an onClickListener
Intent intent = new Intent(context, MyWidgetProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.update, pendingIntent);
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
}
}

Open the AndroidManifest.xml and register your widget similar to the following listing.

<?xml version="1.0" encoding="utf-8"?>


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.vogella.android.widget.example"
android:versionCode="1"
android:versionName="1.0" >
<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>
<uses-sdk android:minSdkVersion="8" />
</manifest>

This attribute specifies that the AppWidgetProvider accepts the ACTION_APPWIDGET_UPDATE broadcast and
specifies the metadata for the widget.

5.3. Validate
Deploy your application on your Android device. Once your application has been deployed use the Android
launcher to install your new widget on the home screen and test it.

6. Collection View Widgets


Collection view widgets add support for the usage of the ListView , StackView and GridView classes in
widgets.
For collection view widgets you need two layout files, 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 an Android service which extends the RemoteViewsService class. This
service requires the android.permission.BIND_REMOTEVIEWS permission.
To connect your views with the service, you use your onUpdate() method in your widget implementation.
You define an intent pointing to the service and use the setRemoteAdapter method on
the RemoteViews class.

Intent intent = new Intent(context, YourRemoteViewsService.class);


intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
views.setRemoteAdapter(apppWidgetId,
R.id.widget_your_id_to_collectionview,
intent)

7. Enabling a Widget for the Lock Screen


Since Android 4.2, it is possible to add home screen application widgets to the lock screen of an Android device.
For that you just need to additionally declare a widget to support the "keyguard"" category in
the android:widgetCategory attribute in the AppWidgetProviderInfo XML file. The following code shows
an example.

<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.

Bundle options = appWidgetManager.getAppWidgetOptions(widgetId);


int category = options.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1);
boolean isLockScreen = category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD;

Using this technique you can decide at runtime whether the widgets your application provides, will look
differently, when they are hosted on the lock screen.
Similarly to how you used the android:initialLayout attribute for defining an initial layout for home screen widgets,
you can use a new android:initialKeyguardLayout attribute for the 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 the widget is initialized.
8. Exercise: Update widget via a service
The following will demonstrate the usage of a service to update the widget.
Create the following UpdateWidgetService class in your project.

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;
public class UpdateWidgetService extends Service {
private static final String LOG = "de.vogella.android.widget.example";
@Override
public void onStart(Intent intent, int startId) {
Log.i(LOG, "Called");
// create some random data
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this
.getApplicationContext());
int[] allWidgetIds = intent
.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
ComponentName thisWidget = new ComponentName(getApplicationContext(),
MyWidgetProvider.class);
int[] allWidgetIds2 = appWidgetManager.getAppWidgetIds(thisWidget);
Log.w(LOG, "From Intent" + String.valueOf(allWidgetIds.length));
Log.w(LOG, "Direct" + String.valueOf(allWidgetIds2.length));
for (int widgetId : allWidgetIds) {
// create some random data
int number = (new Random().nextInt(100));
RemoteViews remoteViews = new RemoteViews(this
.getApplicationContext().getPackageName(),
R.layout.widget_layout);
Log.w("WidgetExample", String.valueOf(number));
// Set the text
remoteViews.setTextViewText(R.id.update,
"Random: " + String.valueOf(number));
// 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;
}
}

Add this class as a Service to your AndroidManifest.xml file.

<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;
public class MyWidgetProvider extends AppWidgetProvider {
private static final String LOG = "de.vogella.android.widget.example";
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
Log.w(LOG, "onUpdate method called");
// Get all ids
ComponentName thisWidget = new ComponentName(context,
MyWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
// Build the intent to call the service
Intent intent = new Intent(context.getApplicationContext(),
UpdateWidgetService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds);
// Update the widgets via the service
context.startService(intent);
}
}

Once called, this service will update all widgets. You can click on one of the widgets to update all widgets.
http://www.vogella.com/tutorials/AndroidWidgets/article.html

Вам также может понравиться