Beruflich Dokumente
Kultur Dokumente
Tema 18
Widgets
TEMA 18. WIDGETS
Introducción
Los widgets tienen ciertas limitaciones. Debido a que viven en la Pantalla de Inicio, deben
coexistir con los gestos que se aplican para navegar en dicha Pantalla de Inicio. Esto implica
que un widget solo puede gestionar gestos de deslizamientos verticales (vertical swipe) y
toques (touch).
Para crear un widget, se necesita, como mínimo:
También se puede implementar una actividad especializada en configurar el widget y que será
iniciada cuando el usuario añada el widget a la Pantalla de Inicio.
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/my_widget_provider_info" />
</receiver>
1
AppWidgetProvider es subclase de BroadcastReceiver.
</appwidget-provider>
En este descriptor se debe especificar el espacio mínimo que ocupará el widget por defecto
(minWidth y minHeight). La Pantalla de Inicio ubica los widgets en una cuadrícula cuyas celdas
tienen un ancho y un alto específico 2. El ancho y alto mínimo especificados serán utilizados
para redondear al alza el tamaño del widget en función del tamaño de estas celdas.
El tamaño del widget también se verá afectado por los márgenes de las celdas. En función del
número de celdas que se ocupen, se puede estimar el tamaño del widget a través de esta
tabla:
Para especificar el tamaño mínimo absoluto del widget se utilizarán los atributos
minResizeWidht y minResizeHeight. Por debajo de estas dimensiones, el widget será
ilegible o no podrá ser utilizado. Estos atributos son introducidos en Android 3.1 y permiten
que el usuario redimensione el widget a un tamaño menor que el tamaño por defecto,
especificado a través de minWidth y minHeight.
La frecuencia de actualización de los datos que muestra el widget es especificada a través del
atributo updatePeriodMillis (en milisegundos), lo cual se traduce en la frecuencia con la que
el sistema invoca al método onUpdate() en el proveedor (AppWidgetProvider). No se
garantiza que el momento de la actualización sea exacto, por lo que se recomienda que se
2
El número de filas y columnas de esta cuadrícula, así como el tamaño de cada celda, varía en función del
dispositivo. Por ejemplo, los dispositivos móviles utilizan una cuadrícula de 4x4 mientras que las tablets utilizan una
cuadrícula de 8x7. Por lo tanto, para que un widget sea compatible con la mayor cantidad de dispositivos distintos
posible deberá tener un tamaño mínimo no superior a 4x4 celdas.
utilice la frecuencia de actualización más baja posible (se ha de tener en cuenta que un
dispositivo en stand-by será encendido para poder realizar la actualización).
Para realizar actualizaciones frecuentes del widget solamente cuando el dispositivo está
encendido se deberá establecer updatePeriodMillis="0" y utilizar una alarma que lance un
Intent que reciba el proveedor del widget, a través de AlarmManager. La alarma deberá ser
de tipo ELAPSED_REALTIME o RTC, ya que estos tipos solo envían la alarma si el dispositivo
está encendido.
Los atributos initialLayout e initialKeyguardLayout referencian a los layouts que
definen el aspecto del widget en la Pantalla de Inicio y en la Pantalla de Bloqueo del
dispositivo 3 respectivamente. Estos layouts se consideran iniciales ya que son los que el
sistema muestra inmediatamente, mientras el widget se inicializa y es capaz de actualizar su
layout.
Para definir una actividad que permita que al usuario configurar las propiedades del widget en
el momento en que sea creado, se utiliza el atributo configure.
El atributo previewImage referencia a una imagen que será utilizada como previsualización del
aspecto del widget una vez configurado, y que será mostrada por el sistema cuando el usuario
navegue por la lista de widgets disponibles. En caso de que esta imagen no exista, el sistema
utilizará el icono launcher asociado a la aplicación. Esta imagen puede ser creada gracias a la
aplicación Widget Preview, que viene preinstalada en los AVDs.
Para especificar las pantallas (de Inicio y/o de Bloqueo) en las que el widget aparecerá, se
utiliza el atributo widgetCategory. El valor por defecto es home_screen.
3
A partir de Android 4.2 se permite la inclusión de widgets en la Pantalla de Bloqueo.
El layout inicial del widget será almacenado como archivo XML en el directorio “res/layout/”.
Debido a que los layouts de los widgets se basan en RemoteViews 4, existen ciertos tipos de
componentes y layouts que no pueden formar parte de los widgets. Los objetos de tipo
RemoteViews (y por tanto los widgets) solo soportan los layouts FrameLayout,
LinearLayout, RelativeLayout y GridLayout, así como las clases (vistas) AnalogClock,
Button, Chronometer, ImageButton, ImageView, ProgressBar, TextView, ViewFlipper,
ListView, GridView, StackView y AdapterViewFlipper. Además, no están soportadas clases
que hereden de las anteriores. También se puede usar ViewStub, vista que es invisible y que
no ocupa espacio, para inflar en segundo plano recursos en tiempo de ejecución.
Los widgets deben especificar cierto margen ya que no deben alcanzar los límites de la pantalla
del dispositivo o solapar con otros widgets.
A partir de Android 4.0, el sistema asigna automáticamente cierto padding 5 a los widgets para
proporcionar un mejor alineamiento de iconos y widgets. Es muy recomendable aplicar este
comportamiento, para lo cual será necesario asociar targetSdkVersion al nivel de API 14 (o
superior).
Para crear un único layout que tenga márgenes específicos para versiones anteriores de
Android y que no tenga márgenes a partir de Android 4.0 (ya que tendrá padding), se
establecerá targetSdkVersion="14" y se creará un layout similar al siguiente, que referencie
a recursos de tipo dimension en los márgenes:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/my_widget_background"
android:orientation="horizontal" >
</LinearLayout>
</FrameLayout>
4
RemoteViews es una clase que describe una jerarquía de vistas (View) que pueden ser mostradas en otro proceso
(como por ejemplo, en una aplicación host tipo launcher). Proporciona métodos para inflar remotamente el layout
así como para modificar las propiedades de sus componentes.
5
Este padding es el espacio libre que existe entre el límite del widget y el límite del conjunto de celdas que ocupe
en la pantalla.
Además, se crearán dos recursos de tipo dimension que serán almacenados en las carpetas
“res/values/” y “res/values-v14/” para proporcionar márgenes específicos en versiones
anteriores a Android 4.0 y para no añadir padding extra en versiones posteriores,
respectivamente:
• Recurso res/values/dimens.xml:
• Recurso res/values-v14/dimens.xml:
El proveedor del widget es una clase que extenderá a AppWidgetProvider quien a su vez
hereda de BroadcastReceiver. Es, por lo tanto, un receptor de broadcasts y así tiene que ser
declarado en el manifiesto, a través de <receiver>, tal y como ya se ha comentado
previamente.
El proveedor recibirá solamente los eventos de broadcast que son relevantes para el widget,
como por ejemplo cuándo este es actualizado, habilitado, deshabilitado o borrado. Cuando
estos eventos ocurren, se invocan a los siguientes métodos callback en el proveedor del
widget:
Tal y como ya se ha comentado, el método más importante del proveedor será onUpdate(),
que recibe como parámetro un array con los identificadores de todas las instancias del widget
que haya. Por ejemplo, en el caso de un widget que muestre un número aleatorio distinto en
cada instancia, será necesario realizar la inicialización para cada una de dichas instancias del
widget, de un modo similar al siguiente:
Log.d(TAG, "onUpdate");
final int N = appWidgetIds.length;
// Se realiza la inicialización de cada instancia del widget que
// exista
for (int i = 0; i < N; i++) {
int appWidgetId = appWidgetIds[i];
Log.d(TAG, "onUpdate. appWidgetId=" + appWidgetId);
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
6
Existe un bug en Android 1.5 que hace que no se invoque el método onDeleted(). Este bug puede ser solventado
implementando el método onReceive() para invocar a onDeleted() tal y como se explica en este post:
https://groups.google.com/forum/?fromgroups=#!msg/android-developers/Nl0e06rDCRY/4nAh3xnKBeQJ
En el código anterior puede observarse cómo se inicializan todas las instancias del widget. La
inicialización consiste en acceder a su layout (a través de RemoteViews) para añadir un
PendingIntent que hará un broadcast al proveedor del widget cada vez que se pulse el
widget (y que hará que se actualice el número aleatorio mostrado), ya que se añade como
evento on-click a rootWidgetLayout (identificador del elemento raíz del layout del widget;
por ejemplo, un RelativeLayout). Además, se actualiza el número que se muestra,
accediendo a una TextView contenida en el layout.
Todas las instancias que existan del widget serán actualizadas a la vez. Como sólo existe un
único valor del atributo updatePeriodMillis, el evento de actualización se envía a la vez a
todas las instancias. Pese a que se hayan añadido en distintos momentos, todas se actualizarán
con el periodo establecido por la primera instancia añadida.
Pese a que el proveedor gestiona los broadcasts del widget automáticamente, invocando a los
métodos callback correspondientes, se podrá modificar este comportamiento sobrescribiendo
el método onReceive() para gestionar los Intent con las acciones
ACTION_APPWIDGET_UPDATE, ACTION_APPWIDGET_DELETED, ACTION_APPWIDGET_ENABLED,
ACTION_APPWIDGET_DISABLED, ACTION_APPWIDGET_OPTIONS_CHANGED 7.
7
Se deberán declarar los correspondientes filtros para el proveedor, en el manifiesto del widget.
Como ya se ha mencionado, se puede crear una actividad que configure los parámetros
iniciales del widget y que será iniciada por la aplicación host cuando se añada cada instancia
del widget.
<activity android:name=
"com.cursoandroid.ui.widget.MyWidgetConfigurationActivity">
<intent-filter>
<action android:name=
"android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
Además, esta
actividad también debe referenciarse en el archivo XML de
AppWidgetProviderInfo a través del atributo android:configure 8.
Cuando la aplicación host del widget invoca a la actividad de configuración, espera que esta
devuelva un resultado a través de un Intent, que deberá contener el identificador del widget
(guardado en los extras como AppWidgetManager.EXTRA_APPWIDGET_ID). Además se ha de
tener en cuenta que el método callback del widget onUpdate() no será invocado cuando el
widget sea creado, ya que el sistema no envía el broadcast ACTION_WIDGET_UPDATE cuando es
invocada la actividad de configuración. Por lo tanto, esta actividad, una vez haya configurado
el widget, deberá solicitar a AppWidgetManager que actualice el widget por primera vez. A
partir de ese momento, el método onUpdate() será invocado normalmente.
8
La referencia a la actividad de configuración en el archivo XML de AppWidgetProviderInfo debe incluir el espacio
de nombres (el nombre del paquete) ya que la aplicación host invoca a dicha actividad desde fuera del contexto de
la misma.
Una vez completada la configuración del widget, este deberá ser actualizado. Para ello, se
obtendrá AppWidgetManager, así como el layout del widget a través de RemoteViews, y se
actualizará a través de updateAppWidget(int, RemoteViews):
Para finalizar la actividad, se deberá crear el Intent que se devolverá a la aplicación host:
El aspecto del widget podrá variar en función del host al cual se añada. Además de poder
asociar distintos layouts iniciales a través de los atributos android:initialLayout y
android:initialKeyguardLayout, se podrá detectar en tiempo de ejecución dónde se está
mostrando el widget a través del método AppWidgetManager.getAppWidgetOptions() 10,
que devuelve un Bundle que contiene diversas propiedades y características del widget. De
este modo, se podrán mantener diferenciados los distintos layouts del widget en las sucesivas
actualizaciones del mismo, así como establecer diferentes propiedades:
9
Para que el widget pueda ser añadido a ambas pantallas, se especificará
android:widgetCategory="home_screen|keyguard"
10
Método disponible solo a partir de Android 4.2.