Inicio Android Interfaz de usuario en Android: Widgets (II)

Interfaz de usuario en Android: Widgets (II)

por sgoliver

En un artículo anterior del curso ya vimos cómo construir un widget básico para Android, y prometimos que dedicaríamos un artículo adicional a comentar algunas características más avanzadas de este tipo de componentes. Pues bien, en este segundo artículo sobre el tema vamos a ver cómo podemos añadir los siguientes elementos y funcionalidades al widget básico que ya construímos:

  • Pantalla de configuración inicial.
  • Datos actualizables de forma periodica.
  • Eventos de usuario.

Como sabéis, intento simplificar al máximo todos los ejemplos que utilizo en este curso para que podamos centrar nuestra atención en los aspectos realmente importantes. En esta ocasión utilizaré el mismo criterio y las únicas características (aunque suficientes para demostrar los tres conceptos anteriores) que añadiremos a nuestro widget serán las siguientes:

  1. Añadiremos una pantalla de configuración inicial del widget, que aparecerá cada vez que se añada una nueva instancia del widget a nuestro escritorio. En esta pantalla podrá configurarse únicamente el mensaje de texto a mostrar en el widget.
  2. Añadiremos un nuevo elemento de texto al widget que muestre la hora actual. Esto nos servirá para comprobar que el widget se actualiza periodicamente.
  3. Añadiremos un botón al widget, que al ser pulsado forzará la actualización inmediata del mismo.

Empecemos por el primer punto, la pantalla de configuración inicial del widget. Y procederemos igual que para el diseño de cualquier otra actividad android, definiendo su layout xml. En nuestro caso será muy sencilla, un cuadro de texto para introducir el mensaje a personalizar y dos botones, uno para aceptar la configuración y otro para cancelar (en cuyo caso el widget no se añade al escritorio). En esta ocasión llamaremos a este layout «widget_config.xml«. Veamos como queda:

<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">

    <TextView android:id="@+id/LblMensaje"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="@string/mensaje_personalizado" />

    <EditText android:id="@+id/TxtMensaje"
              android:layout_height="wrap_content"
              android:layout_width="match_parent" />

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal" >

        <Button android:id="@+id/BtnAceptar"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/aceptar" />

        <Button android:id="@+id/BtnCancelar"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/cancelar" />

    </LinearLayout>

</LinearLayout>

Una vez diseñada la interfaz de nuestra actividad de configuración tendremos que implementar su funcionalidad en java. Llamaremos a la clase WidgetConfig, su estructura será análoga a la de cualquier actividad de Android, y las acciones a realizar serán las comentadas a continuación. En primer lugar nos hará falta el identificador de la instancia concreta del widget que se configurará con esta actividad. Este ID nos llega como parámetro del intent que ha lanzado la actividad. Como ya vimos en un artículo anterior del curso, este intent se puede recuperar mediante el métdo getIntent() y sus parámetros mediante el método getExtras(). Conseguida la lista de parámetros del intent, obtendremos el valor del ID del widget accediendo a la clave AppWidgetManager.EXTRA_APPWIDGET_ID. Veamos el código hasta este momento:

public class WidgetConfig extends Activity {

    private int widgetId = 0;

    private Button btnAceptar;
    private Button btnCancelar;
    private EditText txtMensaje;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.widget_config);

        //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
        widgetId = params.getInt(
                AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);

        //Establecemos el resultado por defecto (si se pulsa el botón 'Atrás'
        //del teléfono será éste el resultado devuelto).
        setResult(RESULT_CANCELED);

        //...
    }
}

En el código también podemos ver como aprovechamos este momento para establecer el resultado por defecto a devolver por la actividad de configuración mediante el método setResult(). Esto es importante porque las actividades de configuración de widgets deben devolver siempre un resultado (RESULT_OK en caso de aceptarse la configuración, o RESULT_CANCELED en caso de salir de la configuración sin aceptar los cambios). Estableciendo aquí ya un resultado RESULT_CANCELED por defecto nos aseguramos de que si el usuario sale de la configuración pulsando el botón Atrás del teléfono no añadiremos el widget al escritorio, mismo resultado que si pulsáramos el botón Cancelar de nuestra actividad.

Como siguiente paso recuperamos las referencias a cada uno de los controles de la actividad de configuración:

//Obtenemos la referencia a los controles de la pantalla
btnAceptar = (Button)findViewById(R.id.BtnAceptar);
btnCancelar = (Button)findViewById(R.id.BtnCancelar);
txtMensaje = (EditText)findViewById(R.id.TxtMensaje);

Por último, implementaremos las acciones de los botones Aceptar y Cancelar. En principio, el botón Cancelar no tendría por qué hacer nada, tan sólo finalizar la actividad mediante una llamada al método finish() ya que el resultado CANCELED ya se ha establecido por defecto anteriormente:

//Implementación del botón "Cancelar"
btnCancelar.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View arg0) {
        //Devolvemos como resultado: CANCELAR (RESULT_CANCELED)
        finish();
    }
});

En el caso del botón Aceptar tendremos que hacer más cosas:

  1. Guardar de alguna forma el mensaje que ha introducido el usuario.
  2. Actualizar manualmente la interfaz del widget según la configuración establecida.
  3. Devolver el resultado RESULT_OK aportanto además el ID del widget.

Para el primer punto nos ayudaremos de la API de Preferencias que describimos en el artículo anterior. En nuestro caso, guardaremos una sóla preferencia cuya clave seguirá el patrón «msg_IdWidget«, esto nos permitirá distinguir el mensaje configurado para cada instancia del widget que añadamos a nuestro escritorio de Android.

El segundo paso indicado es necesario debido a que si definimos una actividad de configuración para un widget, será ésta la que tenga la responsabilidad de realizar la primera actualización del mismo en caso de ser necesario. Es decir, tras salir de la actividad de configuración no se lanzará automáticamente el evento onUpdate() del widget (sí se lanzará posteriormente y de forma periódica según la configuración del parámetro updatePeriodMillis del provider que veremos más adelante), sino que tendrá que ser la propia actividad quien fuerce la primera actualización. Para ello, simplemente obtendremos una referencia al widget manager de nuestro contexto mediente el método AppWidgetManager.getInstance() y con esta referencia llamaremos al método estático de actualización del widget MiWidget.actualizarWidget(), que actualizará los datos de todos los controles del widget (lo veremos un poco más adelante).

Por último, al resultado a devolver (RESULT_OK) deberemos añadir información sobre el ID de nuestro widget. Esto lo conseguimos creando un nuevo Intent que contenga como parámetro el ID del widget que recuperamos antes y estableciéndolo como resultado de la actividad mediante el método setResult(resultado, intent). Por último llamaremos al método finish() para finalizar la actividad.

Con estas indicaciones, veamos cómo quedaría el código del botón Aceptar:

//Implementación del botón "Aceptar"
btnAceptar.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View arg0) {
            //Guardamos el mensaje personalizado en las preferencias
            SharedPreferences prefs =
                getSharedPreferences("WidgetPrefs", Context.MODE_PRIVATE);
            SharedPreferences.Editor editor = prefs.edit();
            editor.putString("msg_" + widgetId, txtMensaje.getText().toString());
            editor.commit();

            //Actualizamos el widget tras la configuración
            AppWidgetManager appWidgetManager =
                AppWidgetManager.getInstance(WidgetConfig.this);
            MiWidget.actualizarWidget(WidgetConfig.this, appWidgetManager, widgetId);

            //Devolvemos como resultado: ACEPTAR (RESULT_OK)
            Intent resultado = new Intent();
            resultado.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
            setResult(RESULT_OK, resultado);
            finish();
        }
});

Ya hemos terminado de implementar nuestra actividad de configuración. Pero para su correcto funcionamiento aún nos quedan dos detalles más por modificar. En primer lugar tendremos que declarar esta actividad en nuestro fichero AndroidManifest.xml, indicando que debe responder a los mensajes de tipo APPWIDGET_CONFIGURE:

<activity android:name=".WidgetConfig">
    <intent-filter>
        <action android:name="android.apwidget.action.APPWIDGET_CONFIGURE"/>
    </intent-filter>
</activity>

Por último, debemos indicar en el XML de configuración de nuestro widget (xml\miwidget_wprovider.xml) que al añadir una instancia de este widget debe mostrarse la actividad de configuración que hemos creado. Esto se consigue estableciendo el atributo android:configure del provider. Aprovecharemos además este paso para establecer el tiempo de actualización automática del widget al mínimo permitido por este parámetro (30 minutos) y el tamaño del widget a 3×2 celdas. Veamos cómo quedaría finalmente:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/miwidget"
    android:minWidth="180dip"
    android:minHeight="110dip"
    android:label="@string/mi_primer_widget"
    android:updatePeriodMillis="3600000"
    android:configure="net.sgoliver.android.widgets.WidgetConfig"
/>

Con esto, ya tenemos todo listo para que al añadir nuestro widget al escritorio se muestre automáticamente la pantalla de configuración que hemos construido. Podemos ejecutar el proyecto en este punto y comprobar que todo funciona correctamente.

Como siguiente paso vamos a modificar el layout del widget que ya construimos en el artículo anterior para añadir una nueva etiqueta de texto donde mostraremos la hora actual, y un botón que nos servirá para forzar la actualización de los datos del widget:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000"
    android:padding="10dp"
    android:layout_margin="5dp" >

    	<LinearLayout android:id="@+id/FrmWidget"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#FFFFFF"
            android:padding="5dp"
            android:orientation="vertical">

            <TextView android:id="@+id/LblMensaje"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="#000000"
                android:text="" />

            <TextView android:id="@+id/LblHora"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="#000000"
                android:text="" />

            <Button android:id="@+id/BtnActualizar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="#000000"
                android:text="@string/actualizar" />

        </LinearLayout>

</FrameLayout>

Hecho esto, tendremos que modificar la implementación de nuestro provider (MiWidget.java) para que en cada actualización del widget se actualicen sus controles con los datos correctos (recordemos que en el artículo anterior dejamos este evento de actualización vacío ya que no mostrábamos datos actualizables en el widget). Esto lo haremos dentro del evento onUpdate() de nuestro provider.

Como ya dijimos, los componentes de un widget se basan en un tipo especial de vistas que llamamos  Remote Views. Pues bien, para acceder a la lista de estos componentes que constituyen la interfaz del widget construiremos un nuevo objeto RemoteViews a partir del ID del layout del widget. Obtenida la lista de componentes, tendremos disponibles una serie de métodos set (uno para cada tipo de datos básicos) para establecer las propiedades de cada control del widget. Estos métodos reciben como parámetros el ID del control, el nombre del método que queremos ejecutar sobre el control, y el valor a establecer. Además de estos métodos, contamos adicionalmente con una serie de métodos más específicos para establecer directamente el texto y otras propiedades sencillas de los controles TextView, ImageView, ProgressBar y Chronometer, como por ejemplo setTextViewText(idControl, valor) para establecer el textode un control TextView. Pueden consultarse todos los métodos disponibles en la documentación oficial de la clase RemoteViews. De esta forma, si por ejemplo queremos establecer el texto del control cuyo id es LblMensaje haríamos lo siguiente:

RemoteViews controles = new RemoteViews(context.getPackageName(), R.layout.miwidget);
controles.setTextViewText(R.id.LblMensaje, "Mensaje de prueba");

El proceso de actualización habrá que realizarlo por supuesto para todas las instancias del widget que se hayan añadido al escritorio. Recordemos aquí que el evento onUpdate() recibe como parámetro la lista de widgets que hay que actualizar.

Dicho esto, creo que ya podemos mostrar cómo quedaría el código de actualización de nuestro widget:

@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);
    }
}

public static void actualizarWidget(Context context,
                    AppWidgetManager appWidgetManager, int widgetId)
{
    //Recuperamos el mensaje personalizado para el widget actual
    SharedPreferences prefs =
            context.getSharedPreferences("WidgetPrefs", Context.MODE_PRIVATE);
    String mensaje = prefs.getString("msg_" + widgetId, "Hora actual:");

    //Obtenemos la lista de controles del widget actual
    RemoteViews controles =
            new RemoteViews(context.getPackageName(), R.layout.miwidget);

    //Actualizamos el mensaje en el control del widget
    controles.setTextViewText(R.id.LblMensaje, mensaje);

    //Obtenemos la hora actual
    Calendar calendario = new GregorianCalendar();
    String hora = calendario.getTime().toLocaleString();

    //Actualizamos la hora en el control del widget
    controles.setTextViewText(R.id.LblHora, hora);

    //Notificamos al manager de la actualización del widget actual
    appWidgetManager.updateAppWidget(widgetId, controles);
}

Como vemos, todo el trabajo de actualzación para un widget lo hemos extraido a un método estático independiente, de forma que también podamos llamarlo desde otras partes de la aplicación (como hacemos por ejemplo desde la actividad de configuración para forzar la primera actualización del widget).

Además quiero destacar la última linea del código, donde llamamos al método updateAppWidget() del widget manager. Esto es importante y necesario, ya que de no hacerlo la actualización de los controles no se reflejará correctamente en la interfaz del widget.

Tras esto, ya sólo nos queda implementar la funcionalidad del nuevo botón que hemos incluido en el widget para poder forzar la actualización del mismo. 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 que hemos visto en múltiples ocasiones durante el curso. Sin embargo, en su lugar, tenemos la posibilidad de asociar a un evento (por ejemplo, el click sobre un botón) un determinado mensaje (Pending Intent) de tipo broadcast que será lanzado cada vez que se produzca dicho evento. Además, podremos configurar el widget (que como ya indicamos no es más que un componente de tipo broadcast receiver) para que capture esos mensajes, e implementar en su evento onReceive() las acciones necesarias a ejecutar tras capturar el mensaje. Con estas tres acciones simularemos la captura de eventos sobre controles de un widget.

Vamos por partes. En primer lugar hagamos que se lance un intent de tipo broadcast cada vez que se pulse el botón del widget. Para ello, en el método actualizarWidget() construiremos un nuevo Intent asociándole una acción personalizada, que en nuestro caso llamaremos por ejemplo «net.sgoliver.android.widgets.ACTUALIZAR_WIDGET«. 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 (recordemos que podemos tener varias instancias del mismo widget en el escritorio). Por último crearemos el PendingIntent mediante el método getBroadcast() y lo asociaremos al evento onClick del control llamando a setOnClickPendingIntent() pasándole el ID del control, en nuestro caso el botón de Actualizar. Veamos cómo queda todo esto dentro del método actualizarWidget():

Intent intent = new Intent("net.sgoliver.android.widgets.ACTUALIZAR_WIDGET");
intent.putExtra(
	     AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);

PendingIntent pendingIntent =
	PendingIntent.getBroadcast(context, widgetId,
		intent, PendingIntent.FLAG_UPDATE_CURRENT);

controles.setOnClickPendingIntent(R.id.BtnActualizar, pendingIntent);

También podemos hacer por ejemplo que si pulsamos en el resto del espacio del widget (el no ocupado por el botón) se abra automáticamente la actividad principal de nuestra aplicación. Se haría de forma análoga, con la única diferencia que en vez de utilizar getBroadcast() utilizaríamos getActivity() y el Intent lo construiríamos a partir de la clase de la actividad principal:

Intent intent2 = new Intent(context, MainActivity.class);
PendingIntent pendingIntent2 =
	PendingIntent.getActivity(context, widgetId,
		intent2, PendingIntent.FLAG_UPDATE_CURRENT);

controles.setOnClickPendingIntent(R.id.FrmWidget, pendingIntent2);

Ahora vamos a declarar en el Android Manifest este mensaje personalizado, de forma que el widget sea capaz de capturarlo. Para ello, añadiremos simplemente un nuevo elemento <intent-filter> con nuestro nombre de acción personalizado dentro del componente <receiver> que ya teníamos definido:

<receiver android:name=".MiWidget" android:label="Mi Primer Widget">
   <intent-filter>
      <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
   </intent-filter>
   <intent-filter>
      <action android:name="net.sgoliver.android.widgets.ACTUALIZAR_WIDGET"/>
   </intent-filter>
   <meta-data
         android:name="android.appwidget.provider"
         android:resource="@xml/miwidget_wprovider" />
</receiver>

Por último, vamos a 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 menasje 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 llamaremos 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("net.sgoliver.android.widgets.ACTUALIZAR_WIDGET")) {
        //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, por fin, hemos ya finalizado la construcción de nuestro widget android y podemos ejecutar el proyecto de Eclipse para comprobar que todo funciona correctamente, tanto para una sola instancia como para varias instancias simultaneas.

Cuando añadamos el widget al escritorio nos aparecerá la pantalla de configuración que hemos definido:

configuracion-widget

 

Una vez introducido el mensaje que queremos mostrar, pulsaremos el botón Aceptar y el widget aparecerá automáticamente en el escritorio con dicho mensaje, la fecha-hora actual y el botón Actualizar.

demo-widget-2

Un comentario final, la actualización automática del widget se ha establecido a la frecuencia mínima que permite el atributo updatePeriodMillis del widget provider, que son 30 minutos. Por tanto es dificil y aburrido esperar para verla en funcionamiento mientras probamos el widget en el emulador. Pero funciona, os lo aseguro. De cualquier forma, esos 30 minutos pueden ser un periodo demasiado largo de tiempo según la funcionalidad que queramos dar a nuestro widget, que puede requerir tiempos de actualización mucho más cortos (ojo con el rendimiento y el gasto de batería). Más adelante, cuando hablemos de Alarmas, veremos una técnica que nos permitirá actualizar los widgets sin esa limitación de 30 minutos. Hasta entonces, espero que el tema os sea de utilidad y que os haya parecido interesante.

Puedes consultar y/o descargar el código completo de los ejemplos desarrollados en este artículo accediendo a la página del curso en GitHub.

Curso de Programación Android en PDF

Este curso también está disponible en PDF. Descubre cómo conseguirlo…

¿Te ha sido de utilidad el Curso de Programación Android? ¿Quieres colaborar de forma económica con el proyecto? Puedes contribuir con cualquier cantidad, unos céntimos, unos euros, cualquier aportación será bienvenida. Además, si tu aportación es superior a una pequeña cantidad simbólica recibirás como agradecimiento un documento con la última versión del curso disponible en formato PDF. Sea como sea, muchas gracias por colaborar!

Más información:

También te puede interesar

26 comentarios

iphone-sucks 28/03/2011 - 21:44

Excelente articulo, muy sencillo y facil de seguir, no conozco nada de prog. Java, pero quedo muy entendible el tutorial

Responder
Desarrollo en Android | sgoliver.net blog 30/03/2011 - 16:14

[…] Interfaz de usuario en Android: Widgets (II) […]

Responder
Baldomero 06/04/2011 - 9:33

Hola,

He seguido tu tutorial, pero no se como hacer para tener un widget con dos botones que realicen diferentes acciones.

Un saludo

Responder
Baldomero 06/04/2011 - 9:43

He resuelto mi problema creando una entrada intent-filter en el manifest con una accion por cada boton, es esa la forma correcta?

Responder
ECorn 19/04/2011 - 1:44

Excelente Tutorial, ¿Como puedo reducir el tiempo de actualizacion del widget?, ya que quiero informar la ubicacion cada 5 min.

Responder
GM 06/06/2011 - 19:10

Hola, muy buenos tus tutoriales. He hecho varios, pero me confunde un poco el parametro Context, creo que se refiere a la actividad en la que esta definido el metodo que hace uso de ese Context, o me equivoco?

Responder
Adrian 25/08/2011 - 11:28

Hola. Los tutoriales son muy interesantes. Te informo de un pequeño error en el código que me ha traído algún quebradero de cabeza. Donde defines el atributo «android:configure» en el provider, tienes escrito:

La última línea debería ser:
android:configure=»net.sgoliver.WidgetConfig»

¿Me equivoco? También depende de si realmente tienes una carpeta android en ese paquete.

Saludos.

Responder
Adrian 25/08/2011 - 13:12

Olvida el último comentario. Efectivamente tu paquete es «net.sgoliver.android»

Mis disculpas.

Responder
SirNeo 07/03/2012 - 15:14

Muy buenos tutoriales. Un excelente trabajo!!

Hay algo que no veo, cuando pulso en el botón Actualizar, no se actualiza los datos del widget, ¿Qué me puede estar fallando?

Responder
juampa 14/03/2012 - 19:21

@SirNeo
En el método onReceive(…), falta incluir una última línea:

super.onReceive(context, intent);

Creo que por eso no se te están actualizando los datos

Responder
David 02/10/2012 - 16:36

Saludos.

All igual que SirNeo soy incapaz de ver cambios en la hora tras pinchar el boton de actualizar, incluso añadiendo la linea de codigo sugerida: super.onReceive(context, intent);

He probado a importar el codigo fuente original y me veo con el mismo problema.
¿Que podria estar fallando?

Responder
Alberto 21/12/2012 - 16:03

Muy bueno!
Existe alguna forma de que el mismo Activity sea Main de la aplicacion y ademas el configurador de Widget?

Responder
Luis 18/01/2013 - 20:48

Hola,
Excelente esta información, pero solo una duda, como se haría para abrir la app cuando le den click al widget???

Saludos.

Responder
Guillermo 02/01/2014 - 19:01

Tengo una duda con esto de los widgets. Me gustaría incluir en el layout de este un control personalizado que creé extendiendo de un linearlayout (como explicaste en la segunda parte de controles personalizados) y acceder a él, pero no sé como. Si intento añadirlo como un view más al layout el widget da error al cargarse, y aunque así funcionase no sabría como acceder a un método del control personalizado. Alguien me podría ayudar? Muchas gracias

Responder
Juan 15/03/2014 - 6:24

Hola me surge una duda, con el ejemplo este si una vez colocado el widget me voy a Ajustes->aplicaciones y fuerzo el cierre de la aplicación el botón actualizar del widget deja de responder, y para que vuelva a responder hay que crear el widget de nuevo, o reiniciar el telefono.

Entonces mi pregunta es, ¿de qué forma se puede implementar el widget, para que aún forzando el cierre de la aplicación el botón actualizar del widget no deje de responder?

Un saludo y muchas gracias.

Responder
jmark 21/03/2014 - 22:35

Estimado, el onReceive no me funciona. Incluso me quita el texto que aparece la hora.

Responder
facufref 19/04/2014 - 5:28

Yo tenia el mismo problema que todos para actualizar el widget, hasta que me di cuenta que el error era mio. El metodo «net.sgoliver.android.widgets.ACTUALIZAR_WIDGET» se encarga de actualizar el widget, pero en nuestros proyectos lleva otro nombre, seria: «DireccionDeNuestroProyecto.ACTUALIZAR_WIDGET», la que es particular para cada caso. Por eso hay que asegurarse de modificar adecuadamente la direccion, EN ESPECIAL en el MANIFEST, donde tambien aparece el comando que menciono, y en mi caso es lo que me causaba el error.

Responder
Jordi 28/05/2014 - 18:52

«Para el primer punto nos ayudaremos de la API de Preferencias que describimos en el artículo anterior.»
El artículo anterior era Widgets I, y la API de Preferencias se trata en el siguiente, según el índice del curso.

Saludos y gracias.

Responder
admin 29/05/2014 - 10:35

Siento la confusión, me refiero al artículo anterior en el blog, no en el orden de los capítulos del curso. La publicación en el blog de los distintos temas no siempre ha seguido el mismo orden que el índice del curso.

Responder
Raúl 28/07/2014 - 11:04

Hola.

Tengo hecho todo el tutorial pero cuando pulso a actualizar no me actualiza. ¿Por que?

Responder
Antonio 15/11/2014 - 23:37

Me imagino que es algo tarde, pero el error que hace que no se actualice el texto está en el método OnReceive(…). Falta la llamada a super.onReceive(context, intent)

Bajo mi humilde opinión.

Saludos.

Responder
Manuel 01/06/2015 - 13:05

Estoy usando como API mínima la 17 (Android 4.2) y hasta la 22 (Android 5.1) y probando las aplicaciones en un movil con Galaxy S2 con Android 4.4.4 y un Nexus 7 2012 con la 5.1. En general me funciona todo tal y como se describe en la explicación (tanto este como los ejemplos anteriores), se crea el widget y se actualiza al pulsar el botón, puedo crear varios y se actualizan bien.

El problema viene al actualizar la aplicación o reiniciar el dispositivo, en ese momento solo se ve el botón del widget, el resto de elementos estan vacios. Al pulsar el botón de widget no sucede nada, he colocado avisos en el log al principio de cada método/función de la clase y ninguno se inicia.

He probado a crear otro y hacer que en «onReceive» actualize este y el anterior:
actualizarWidget(context, widgetManager, widgetId);
actualizarWidget(context, widgetManager, widgetId – 1);

Y así el widget muerto vuelve a la vida, pero no deberia tener que hacer esos apaños raros para que funcionase. Supongo que siendo el ejemplo de 2011 el método de manipulación de un widget puede haber cambiado.

¿Como puedo solucionar este problema? ¿Actualizarás este ejemplo como estás haciendo con los demás?

Responder
Curso Programación Android por Salvador Gómez – Indice de Contenidos | Miguel Moyetones 17/06/2015 - 17:53

[…] Interfaz de usuario en Android: Widgets (II) [v3] […]

Responder
luna blanco 14/02/2016 - 15:58

No funciona, el código donde me lo puedo descargar, por si hubiese echo algo mal

Responder
Javier 06/03/2018 - 16:20

Excelente Tutorial.

tengo una consulta.
cuando fuerzo un cierre de aplicacion el widget deja de funcionar, como comentan en el hilo, pero al acceder a la aplicacion vuelve a responder el widget.
pero si ago un apagado del telefono al reiniciar, el widget esta sin datos, como ago para que vuelva a responder tras un reinicio de telefono?

y no quiero que el widget se actualice automaticamente, solo cuando presiono en el boton, esto seria unicamente quitando la linea «android:updatePeriodMillis=»3600000″» o hay que indicar algo mas?

Saludos.
Javier,,

Responder
Alvaro 27/10/2021 - 0:57

Para quien no le funcione el botón, desde oreo no se permiten intents implicitos. Hay que definir qué clase va a gestionarlo. Tras definir el primer intent, hay que añadir:

ComponentName componentName = new ComponentName(context, MiWidget.class);
intent.setComponent(componentName);

Lo he descubierto usando el logcat después de 4 horas volviéndome loco porque el botón no hacía nada (logcat devolvía un error diciendo «Background execution not allowed: receiving Intent»)

Responder

Dejar un comentario

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información. Aceptar Más Información

Política de Privacidad y Cookies