AppWidgets

Anuncio
AppWidgets
Índice
1
AppWidgets..................................................................................................................2
2
Crear un Widget........................................................................................................... 3
3
2.1
Definición XML del Widget....................................................................................3
2.2
Layout del Widget....................................................................................................4
2.3
Implementación de la funcionalidad del Widget..................................................... 5
2.4
Manifest................................................................................................................... 6
Actualización del Widget............................................................................................. 7
3.1
4
RemoteViews...........................................................................................................8
Eventos de actualización.............................................................................................. 8
4.1
Abrir una aplicación...............................................................................................10
5
Servicio de actualización............................................................................................10
6
Actividad de configuración........................................................................................ 11
Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved.
AppWidgets
En los escritorios de Android es posible colocar un tipo especial de aplicaciones
denominadas Widgets. Estos Widgets permiten mostrar al usuario una información (o una
especie de interfaz reducida) sin necesidad de abrir una aplicación. Ocupan un
determinado tamaño en el escritorio, por lo que es posible tener en el mismo escritorio
colocados a la vez varios de estos elementos. Son muy útiles para mostrar información
rápida al usuario, como por ejemplo la hora, los últimos correos, últimos eventos del
calendario, etc., y que al pulsar sobre ellos se abra la aplicación completa para poder
interactuar.
1. AppWidgets
Los widgets, que desde el punto de vista del programador son AppWidgets, son pequeños
interfaces de programas Android que permanecen en el escritorio del dispositivo móvil.
Para añadir alguno sobra con hacer una pulsación larga sobre un área vacía del escritorio
y seleccionar la opción "widget", para que aparezca la lista de todos los que hay
instalados y listos para añadir. En la versión 4 de Android para añadir widgets hay que ir
al menú de aplicaciones y desde la sección de widgets seleccionar uno y pulsarlo
prolongadamente para arrastrarlo a la zona deseada de la pantalla de inicio.
Seleccionar un (App) Widget
Este es un ejemplo de un widget en el que se muestra un reloj, incluido de serie con
Android:
2
Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved.
AppWidgets
AppWidget reloj de Android
2. Crear un Widget
Para crear un widget se necesitan varios elementos:
• Una definición o configuración de los metadatos del Widget en XML.
• Un layout para definir su interfaz gráfica.
• La implementación de la funcionalidad del Widget (un IntentReceiver).
• Declaración del Widget en el Android Manifest de la aplicación.
2.1. Definición XML del Widget
Los AppWidgets ocupan un determinado tamaño y se refrescan con una determinada
frecuencia, estos son metadatos que hay que declarar en el XML que define el widget. Se
puede añadir como nuevo recurso XML de Android, y seleccionar el tipo Widget. Se
coloca en la carpeta res/xml/. A continuación se incluye la definición XML guardada en
"res/xml/miwidget_conf.xml" de nuestro ejemplo:
<?xml version="1.0" encoding="utf-8"? >
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="146dip"
android:minHeight="72dip"
android:updatePeriodMillis="4320000"
android:label="Mi Widget"
android:initialLayout="@layout/miwidget_layout"
/>
Las propiedades que hemos definido en este XML son las siguientes:
• minWidth y minHeight: ancho y alto mínimo que ocupará el Widget en el escritorio.
• updatePeriodMillis: frecuencia de actualización del Widget en milisegundos.
• label: nombre del Widget que se mostrará en el menú de selección de Android.
• initialLayout: referencia al layout XML con la interfaz del Widget.
•
La pantalla de inicio de Android por defecto está dividida en una matriz de 4x4 celdas
donde se pueden colocar aplicaciones, accesos directos y Widgets. Los Widgets, como
hemos dicho, pueden ocupar más de una celda. Estas celdas tienen una dimensión de
74x74 dip (píxeles independientes del dispositivo). Para indicar el número de celdas que
va a ocupar el Widget tenemos que calcular el número de "dip" que va a ocupar. La
fórmula para calcular esto es muy sencilla:
• ancho_minimo = (num_celdas_ancho * 74) - 2
• alto_minimo = (num_celdas_alto * 74) - 2
Los 2 dip que se le restan son por el borde. Por ejemplo, en nuestro caso, para que ocupe
2 celdas de ancho por 1 celda de alto tenemos que indicar una dimensiones de 146dip x
3
Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved.
AppWidgets
72dip. En los dispositivos donde las dimensiones mínimas que establezcamos no
coincidan exactamente con las celdas de la pantalla, el tamaño de nuestro widget será
extendido para llenar lo que queda libre de las celdas.
La frecuencia de actualización del Widget (updatePeriodMillis) tiene un mínimo
permitido: 30 minutos (1800000 milisegundos). Además, si el dispositivo está en reposo
cuando se realiza la actualización éste se despertará, por lo que puede que se incremente
el gasto de batería si se realiza con mucha frecuencia. Es aconsejable, para este tipo de
prácticas, utilizar en su lugar intents programados mediante un AlarmManager (como
veremos más adelante).
2.2. Layout del Widget
En el XML de configuración de ejemplo, en el campo "initialLayout", se indica que el
layout del widget se encuentra en "res/layout/miwidget_layout.xml". Este es el
layout que define la interfaz gráfica del Widget, su diseño es análogo al del interfaz de
cualquier aplicación normal, pero con unas pequeñas restricciones. Por cuestiones de
diseño de Android, relacionadas con seguridad y eficiencia, los únicos componentes
gráficos permitidos en el layout del widget son:
• Los layouts: FrameLayout, LinearLayout y RelativeLayout.
• Los views: TextView, Button, ImageButton, ImageView, ProgressBar,
AnalogClock y Chronometer.
Es interesante el hecho de que los EditText no están permitidos en el layout del widget.
Para conseguir este efecto lo que se suele hacer es utilizar una imagen que imite este view
y al hacer click, abrir una actividad semi-transparente por encima que sea la que nos
permita introducir el texto.
Desde la versión 3.0 de Android los widget cuentan con nuevas características y mayor
interactividad. Los nuevos componentes que se pueden utilizar son GridView, ListView,
StackView, ViewFlipper, AdapterViewFlipper. Por ejemplo StackView no estaba
presente entre los componentes de las versiones anteriores y es un view que permite
mostrar "tarjetas" e ir pasándolas hacia delante o hacia atrás, como se muestra a
continuación:
4
Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved.
AppWidgets
Widget con StackView
Un ejemplo de layout podría ser el siguiente, en el que se coloca un reloj analógico y un
campo de texto al lado:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/miwidget"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<AnalogClock
android:id="@+id/analogClock1"
android:layout_width="45dp"
android:layout_height="45dp" />
<TextView android:text=""
android:id="@+id/TextView01"
android:textSize="9dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:shadowColor="#000000"
android:shadowRadius="1.2"
android:shadowDx="1"
android:shadowDy="1">
</TextView>
</LinearLayout>
2.3. Implementación de la funcionalidad del Widget
Los AppWidgets no necesitan ninguna actividad, sino que se implementan como
IntentReceivers con IntentFilters que detectan intents con acciones de actualización
de widget como AppWidget.ACTION_APPWIDGET_UPDATE, DELETED, ENABLED,
DISABLED, así como con acciones personalizadas.
5
Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved.
AppWidgets
Android provee la clase AppWidgetProvider (clase derivada de BroadcastReceiver) que
es una alternativa proporcionada por Android para encapsular el procesamiento de los
Intent y proveer de handlers para la actualización, borrado, habilitación y deshabilitación
del widget.
En esta clase deberemos implementar los mensajes a los que vamos a responder desde
nuestro widget, entre los que destacan:
• 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.
En la mayoría de los casos, tendremos que implementar como mínimo el evento
onUpdate(). El resto de métodos dependerán de la funcionalidad de nuestro widget. En
nuestro ejemplo de momento solo vamos a implementar el método "onUpdate", aunque lo
dejaremos vacío. A continuación se incluye un ejemplo del código de nuestro Widget:
public class MiWidget extends AppWidgetProvider {
@Override
public void onUpdate(Context context,
AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// Actualizar el Widget...
}
}
2.4. Manifest
El último paso es declarar el Widget dentro del AndroidManifest.xml de nuestra
aplicación. No necesitamos declarar una actividad principal, sino que solamente
tendremos que declarar el receptor de intents de nuestro widget:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0"
package="es.ua.jtech.av.appwidget">
<uses-sdk android:minSdkVersion="7" />
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<receiver android:name=".MiWidget"
android:label="Frases Widget">
<intent-filter>
<action
android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<intent-filter>
<action android:name="es.ua.jtech.av.ACTION_WIDGET_CLICK"
/>
</intent-filter>
<meta-data android:name="android.appwidget.provider"
6
Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved.
AppWidgets
android:resource="@xml/miwidget_conf" />
</receiver>
</application>
</manifest>
En el ejemplo anterior de manifest se declara nuestro Widget como un elemento
"receiver" dentro de la sección "application". En la etiqueta "name" se indica el nombre
de la clase java con el código del receptor. Además se añaden dos secciones importantes,
una sección "meta-data" donde en "resource" se tiene que indicar el fichero XML que
contiene la descripción del Widget; y otra sección de "intent-filter" para la captura de
eventos. En este ejemplo se capturará el evento de actualización del widget y un evento
personalizado "es.ua.jtech.av.ACTION_WIDGET_CLICK" (que veremos más adelante).
Llegados a este punto ya es posible probar el Widget, añadir instancias al escritorio,
desplazarlo por la pantalla o eliminarlo.
3. Actualización del Widget
Para realizar la actualización del Widget tenemos que modificar su fichero de código para
completar el método "onUpdate" que habíamos dejado vacío en el ejemplo anterior. Es
importante tener en cuenta que es posible que hayan varias instancias de un mismo widget
añadidas al escritorio, por lo que tendremos que actualizarlas todas. El método
"onUpdate" recibe como parámetro un array con la lista de Widgets que hay que
actualizar. Por lo que de forma genérica podríamos realizar la actualización de la forma:
@Override
public void onUpdate(Context context,
AppWidgetManager appWidgetManager,
int[] appWidgetIds)
{
//Iteramos la lista de widgets en ejecución
for (int i = 0; i < appWidgetIds.length; i++)
{
//ID del widget actual
int widgetId = appWidgetIds[i];
//Actualizamos el widget actual
actualizarWidget(context, appWidgetManager, widgetId);
}
}
private void actualizarWidget(Context context,
AppWidgetManager appWidgetManager, int widgetId)
{
// Actualizar un Widget...
}
Al extraer la actualización a un método estático independiente podemos reutilizar este
código para llamarlo desde otras partes de la aplicación, como por ejemplo para realizar
la actualización inicial.
Para realizar la actualización de las vistas de nuestro Widget necesitamos utilizar una
clase especial llamada RemoteViews. A continuación se explica este proceso de
7
Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved.
AppWidgets
actualización.
3.1. RemoteViews
La clase RemoteViews se utiliza para definir y manipular una jerarquía de views que se
encuentra en el proceso de una aplicación diferente. Permite cambiar propiedades y
ejecutar métodos. En el caso de los widgets, los views están alojados en un proceso
(normalmente perteneciente a la pantalla del Home) y hay que actualizarlos desde el
IntentReceiver que definimos para controlar el widget.
La actualización de los valores de los views se realiza por medio de los métodos de la
clase RemoteViews, tales como .setTextViewText(), .setImageViewBitmap(), etc. En
primer lugar crearemos un objeto de tipo RemoteViews al que le pasaremos el
identificador del layout de nuestro Widget, con este objeto ya podemos actualizar las
vistas de este layout, por ejemplo:
RemoteViews updateViews = new RemoteViews(
context.getPackageName(),
R.layout.miwidget_layout);
updateViews.setTextViewText(R.id.TextView01, "Mensaje de prueba");
RemoteViews proporciona métodos para acceder a propiedades como setInt(...),
setBoolean(...), setBitmap(...), etc, y métodos para acceder a Views específicos,
como setTextViewText(...), setTextColor(...), setImageViewBitmap(...),
setChronometer(...), setProgressBar(...), setViewVisibility(...).
Una vez definido el cambio a realizar por medio de RemoteViews, tenemos que aplicar
estas modificaciones utilizando el appWidgetManager que recibimos como parámetro.
Esto es importante, ya que de no hacerlo la actualización de los controles no se reflejará
correctamente en la interfaz del widget. En el siguiente código se muestra un ejemplo
completo en el que se actualiza el campo de texto del Widget con la hora actual:
private void actualizarWidget(Context context,
AppWidgetManager appWidgetManager, int widgetId)
{
RemoteViews updateViews = new RemoteViews(
context.getPackageName(),
R.layout.miwidget_layout);
//Obtenemos la hora actual
Calendar calendario = new GregorianCalendar();
String hora = calendario.getTime().toLocaleString();
//Actualizamos la hora en el control del widget
updateViews.setTextViewText(R.id.TextView01, hora);
//Notificamos al manager de la actualización del widget actual
appWidgetManager.updateAppWidget(widgetId, updateViews);
}
4. Eventos de actualización
8
Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved.
AppWidgets
A los controles utilizados en los widgets de Android, que ya sabemos que son del tipo
RemoteView, no podemos asociar eventos de la forma tradicional. Sin embargo, tenemos
la posibilidad de asociar a un evento (por ejemplo la realización de un click) una
determinada acción de tipo broadcast (PendingIntent) que será lanzada cada vez que se
produzca dicho evento.
Podemos configurar el propio widget para que capture esos mensajes (ya que como
indicamos no es más que un componente de tipo broadcast receiver). Para esto tenemos
que implementar su método "onReceive()" con las acciones necesarias a ejecutar tras
capturar el mensaje. De esta forma "simulamos" la captura de eventos sobre controles de
un widget.
En primer lugar hacemos que se lance un intent broadcast cada vez que se pulse sobre el
widget o algún elemento del mismo. Para ello, en el método "actualizarWidget()"
construiremos un nuevo Intent asociándole una acción personalizada, que en nuestro caso
llamaremos "es.ua.jtech.av.ACTION_WIDGET_CLICK". Como parámetro del nuevo
Intent insertaremos mediante putExtra() el ID del widget actual de forma que más tarde
podamos saber el widget concreto que ha lanzado el mensaje. Por último crearemos el
PendingIntent mediante el método getBroadcast() y lo asociaremos al evento onClick
del control utilizando setOnClickPendingIntent() y pasándole el ID del control.
Veamos cómo queda todo esto:
// Asociamos los 'eventos' al widget
Intent intent = new Intent("es.ua.jtech.av.ACTION_WIDGET_CLICK");
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
PendingIntent pendingIntent =
PendingIntent.getBroadcast(context, widgetId,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
updateViews.setOnClickPendingIntent(R.id.miwidget, pendingIntent);
Es importante que la acción de este intent se añada al Manifest, dentro de la sección
"receiver" del Widget, tendremos que declarar otro "intent-filter" para que capture este
tipo de eventos y responda a ellos:
<intent-filter>
<action android:name="es.ua.jtech.av.ACTION_WIDGET_CLICK" />
</intent-filter>
Por último tenemos que implementar el evento "onReceive()" del widget para actuar en
caso de recibir nuestro mensaje de actualización personalizado. Dentro de este evento
comprobaremos si la acción del mensaje recibido es la nuestra, y en ese caso
recuperaremos el ID del widget que lo ha lanzado, obtendremos una referencia al "widget
manager", y por último llamamos a nuestro método estático de actualización pasándole
estos datos:
@Override
public void onReceive(Context context, Intent intent)
{
if
(intent.getAction().equals("es.ua.jtech.av.ACTION_WIDGET_CLICK"))
9
Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved.
AppWidgets
{
//Obtenemos el ID del widget a actualizar
int widgetId = intent.getIntExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
//Obtenemos el widget manager de nuestro contexto
AppWidgetManager widgetManager =
AppWidgetManager.getInstance(context);
//Actualizamos el widget
if (widgetId != AppWidgetManager.INVALID_APPWIDGET_ID)
actualizarWidget(context, widgetManager,
widgetId);
}
Con esto ya hemos finalizado la emisión y captura de eventos al pulsar sobre el Widget (o
algún view de este Widget). Dentro del método "actualizarWidget" realizaremos las
acciones de actualización, que dependerán del tipo de widget en cuestión que estemos
implementando.
Para actualizaciones periódicas (con una frecuencia inferior a 30 min) se puede utilizar el
AlarmManager que podría ser programado para forzar la actualización del widget,
enviando periódicamente un broadcast con la acción que hayamos definido para tal fin.
4.1. Abrir una aplicación
Al pulsar sobre un widget, en lugar de lanzar un intent tipo broadcast, también podríamos
lanzar un intent que abra un actividad determinada, simplemente realizando:
//Comportamiento al pulsar el widget: lanzar alguna actividad
Intent defineIntent = new Intent(context, Actividad.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
context, 0, defineIntent, 0);
updateViews.setOnClickPendingIntent(R.id.miwidget, pendingIntent);
5. Servicio de actualización
Otra alternativa para refrescar la interfaz gráfica es el uso de un servicio que actualice el
widget. Para ello habría que declararlo en el manifest y declarar la clase del servicio
UpdateService extends Service dentro de la clase MiWidget.
A continuación se muestra un ejemplo de la clase MiWidget que implementa un widget:
public class MiWidget extends AppWidgetProvider
{
@Override
public void onUpdate(Context context, AppWidgetManager
appWidgetManager, int[] appWidgetIds) {
// Inicio de nuestro servicio de actualización:
context.startService(
new Intent(context, UpdateService.class));
}
10
Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved.
AppWidgets
public static class UpdateService extends Service
{
@Override
public int onStartCommand(Intent intent,
int flags, int
startId) {
RemoteViews updateViews = new RemoteViews(
getPackageName(),
R.layout.miwidget_layout);
//Aqui se actualizarían todos los tipos de Views:
updateViews.setTextViewText(R.id.TextView01,
"Valor con el que refrescamos");
// ...
//Y la actualización a través del updateViews
creado:
ComponentName thisWidget = new ComponentName(
this, MiWidget.class);
AppWidgetManager.getInstance(this).updateAppWidget(
thisWidget, updateViews);
return Service.START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
}
6. Actividad de configuración
Algunos widgets muestran una actividad de configuración la primera vez que son
añadidos a la pantalla. Cuando el sistema solicita la configuración de un widget se recibe
un broadcast intent con la acción android.appwidget.action.APPWIDGET_CONFIGURE,
por tanto definiremos en el manifest nuestra actividad con el intent-filter
correspondiente, que la ejecutará al recibir la acción:
<activity android:name=".MiConfigurationActivity">
<intent-filter>
<action
android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>
Esta actividad es una actividad normal, que podemos crear utilizando todos los conceptos
que hemos visto, recoger los datos del usuario y almacenarlos de alguna forma (por
ejemplo utilizando "SharedPreferences").
Para finalizar la actividad podemos colocar botones para aceptar o cancelar. En el caso de
que se cancele tendríamos que realizar lo siguiente:
setResult(RESULT_CANCELED);
finish();
11
Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved.
AppWidgets
Si se presiona el botón de aceptar la actividad debe devolver un Intent que incluya un
extra con el identificador del App Widget, usando la constante EXTRA_APPWIDGET_ID (se
puede obtener a partir del Intent que lanza la actividad). A continuación se muestra el
código que realizaría esta acción:
//Obtenemos el Intent que ha lanzado esta ventana
//y recuperamos sus parámetros
Intent intentOrigen = getIntent();
Bundle params = intentOrigen.getExtras();
//Obtenemos el ID del widget que se está configurando
int widgetId = params.getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
//Desde el botón aceptar...
Intent resultado = new Intent();
resultado.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
setResult(RESULT_OK, resultado);
finish();
Esta actividad de configuración también debe ser declarada en el xml que contiene los
metadatos del widget:
<appwidget-provider
...
android:updatePeriodMillis="3600000"
android:configure="es.ua.jtech.av.appwidget.MiConfigurationActivity"
/>
La actividad será mostrada solamente al añadir el widget a la pantalla por primera vez.
12
Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved.
AppWidgets
13
Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved.
Descargar