Tutoriales Android

En este capítulo seguiremos trabajando con el diseño de la interfaz de usuario. En lugar de tratar aspectos de diseño visual, como hemos hecho en el capítulo anterior, vamos a tratar temas más relacionados con el código. En concreto, nos centraremos en las actividades y las intenciones. Estudiaremos también dos herramientas de gran utilidad para cualquier aplicación: la barra de acciones y la definición de las preferencias de configuración. Además, se tratará un tipo de vista muy práctica aunque algo compleja de manejar: ListView.

Nos vamos a centrar en el ejemplo de aplicación que estamos desarrollando, Asteroides, para añadirle diferentes actividades. A continuación se muestra el esquema de navegación entre actividades que queremos crear: comentar que es un ejemplo…

Objetivos:

  • Describir el conjunto de actividades que forman la interfaz de usuario en una aplicación Android.
  • Mostrar cómo podemos, desde una actividad, invocar a otras y cómo podemos comunicarnos con ellas.
  • Incorporar a nuestras aplicaciones ciertos elementos prácticos, tales como los menús o las preferencias.
  • Describir cómo podemos utilizar y crear iconos en nuestras aplicaciones.
  • Estudiar una vista muy útil en Android: ListView.
  • Describir el uso de intenciones para invocar actividades estándar en Android.

 

Creación de nuevas actividades

El concepto de actividad en Android representa una unidad de interacción con el usuario, es lo que coloquialmente llamamos una pantalla de la aplicación. Una aplicación suele estar formada por una serie de actividades, de forma que el usuario puede ir navegando entre actividades. En concreto, Android suele disponer de un botón (físico o en pantalla) que nos permite volver a la actividad anterior, es decir la actividad que ha creado a la actividad actual.

video[Tutorial] Actividades en Android

Toda actividad ha de tener una vista asociada, que será utilizada como interfaz de usuario. Esta vista suele ser de tipo Layout aunque no es imprescindible, como se verá en el siguiente ejemplo. 

Una aplicación estará formada por un conjunto de actividades independientes, es decir se trata de clases independientes que no comparten variables, aunque todas trabajan para un objetivo común. Otro aspecto importante es que toda actividad ha de ser una subclase de Activity.

Las aplicaciones creadas hasta ahora disponían de una única actividad. Esta era creada automáticamente y se le asignaba la vista definida en res/layout/activity_main.xml. Esta actividad era arrancada al comenzar la aplicación. A medida que nuestra aplicación crezca va a ser imprescindible crear nuevas actividades. En este apartado describiremos como hacerlo. Este proceso se puede resumir en cuatro pasos:

  • Crea un nuevo Layout para la actividad.
  • Crea una nueva clase descendiente de Activity. En esta clase tendrás que indicar que el Layout a visualizar es el desarrollado en el punto anterior.
  • Para que nuestra aplicación sea visible será necesario activarla desde otra actividad.
  • De forma obligatoria tendremos que registrar toda nueva actividad en AndroidManifest.xml

Veamos un primer ejemplo de cómo crear una nueva actividad en la aplicación que estamos desarrollando.

Ejercicio: Implementación de una caja Acerca de

Vamos a crear una caja Acerca de… y visualizarla cuando se pulse el botón adecuado.

1. En primer lugar crea el fichero res/layout/acercade.xml. Para ello pulsa con el botón derecho sobre el explorador del proyecto en la carpeta res/layout y selecciona New > Layout resource file. Indica en File name: acercade.

2. Selecciona la lengüeta Text y copia el siguiente contenido:

<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/TextView01"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Este programa ha sido desarrollado como ejemplo en el curso de Android
para demostrar cómo se pueden lanzar nuevas actividades desde la actividad principal.">
</TextView>

3. Creamos ahora una nueva actividad, que será la responsable de visualizar esta vista. Para ello crea el fichero AcercaDeActivity.java, pulsando con el botón derecho sobre el nombre del paquete de la aplicación y seleccionando New > Java Class. En el campo Name introduce AcercaDeActivity y pulsa Finish. Reemplaza el código por:

public class AcercaDeActivity extends Activity {

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

 Nota sobre Java: Pulsa Alt-Intro  en las dos clases modificadas para que automáticamente se añadan los paquetes que faltan.

4. Pasemos ahora a crear un método en la actividad principal que será ejecutado cuando sea pulsado el botón Acerca de.  

public void lanzarAcercaDe(View view){
    Intent i = new Intent(this, AcercaDeActivity.class);
    startActivity(i);
}

5. Para asociar este método al botón edita el Layout content_main.xml (o content_main.xml en Mis Lugares). Selecciona la lengüeta Design y pulsa sobre el botón Acerca de… y en la vista Properties  busca el atributo onClick e introduce el valor lanzarAcercaDe.

6. Selecciona la lengüeta Text y observa como, en la etiqueta <Button> correspondiente, se ha añadido el atributo:

android:onClick="lanzarAcercaDe" 

NOTA: En caso de que exista algún recurso alternativo para activity_main.xml repite el mismo proceso. 

7. Ejecuta ahora la aplicación y pulsa en el botón Acerca de. Observarás que el resultado no es satisfactorio ¿Qué ha ocurrido?

El problema es que toda actividad que ha de ser lanzada por una aplicación ha de ser registrada en el fichero AndroidManifest.xml. Para registrar la actividad, abre AndroidManifest.xml. Añade el siguiente texto dentro de la etiqueta <application …> </ application>:

<activity   android:name=".AcercaDeActivity"          
            android:label="Acerca de ..."/>

8. Ejecuta de nuevo el programa. El resultado ha de ser similar al mostrado a continuación:

La vista mostrada en el ejemplo anterior no parece muy atractiva. Tratemos de mejorarla aplicando un tema. Como vimos en el capítulo anterior, un tema es una colección de estilos que define el aspecto de una activad o aplicación. Puedes utilizar alguno de los temas disponibles en Android o crear el tuyo propio.

9. En este caso utilizaremos uno de los de Android. Para ello abre AndroidManifest.xml e introduce la línea subrayada:

<activity android:name=".AcercaDeActivity"
          android:label="Acerca de ..."
          android:theme="@android:style/Theme.Dialog"/>

10. Ejecuta de nuevo el programa y observa cómo la apariencia mejora:

Ejercicio: Un escuchador de evento por código

Como acabamos de ver en un layout podemos definir el atributo XML android:onClick que nos permite indicar un método que será ejecutado al hacer click en una vista. A este método se le conoce como escuchador de evento. Resulta muy sencillo y además está disponible en cualquier descendiente de la clase View. Sin embargo esta técnica presenta dos inconvenientes. Solo está disponible para el evento onClick(). La clase View tiene otros eventos (onLongClick(), onFocusChange(), onKey(),…) para los que no se han definido un atributo xml. Entonces, ¿qué hacemos si queremos definir un evento distinto de onClick(). La respuesta la encontrarás este ejercicio:

1. Abre la clase MainActivity.java y añade las líneas que aparecen subrayadas:

public class MainActivity extends Activity {
    private Button bAcercaDe;
    
    @Override public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        bAcercaDe =(Button) findViewById(R.id.button03);
        bAcercaDe.setOnClickListener(new OnClickListener() {
                   public void onClick(View view) {
                        lanzarAcercaDe(null);
                  }
            });
            }
    …

Nota sobre Java: Pulsa Alt-Intro  para que automáticamente se añadan los imports que faltan. Para la clase OnClickListener selecciona android.view.View.OnClickListener.

2. Elimina el atributo añadido al botón:

android:onClick="lanzarAcercaDe" 

3. Ejecuta la aplicación. El resultado ha de ser identico al anterior.

NOTA:  Más adelante se estudiará con más detalle los escuchadores de evento.

Práctica: El botón salir

En el layout activity_main.xml (o content_main.xml en Mis Lugares) hemos introducido un botón con texto “Salir”.  Queremos que cuando se pulse este botón se cierre la actividad. Para cerrar una actividad puedes llamar al método   finish(); Llamar a este método es equivalente a cuando un usuario pulsa la tecla «retorno».

1. Realiza este trabajo utilizando un escuchador de evento por código.

2. Hazlo ahora con el atributo xml android:onClick.

3. Verifica que el resultado es el mismo en ambos casos.

NOTA: No esconveniente que en tus actividades incluyas un botón para cerrarlas. Un dispositivo Android siempre dispone de la tecla «retorno», que tiene la misma función. 

Solución:

1.     Para resolverlo mediante un escuchador por código, añade en el método onCreate() de la clase MainActivity el siguiente código:

Button bSalir =(Button) findViewById(R.id.button04);
bSalir.setOnClickListener(new OnClickListener() {
    public void onClick(View view) {
        finish();
    }
});

2.     Para resolverlo con el atributo onClick, añade en MainActivity el método:

public void salir(View view){
    finish();
}

Y añade el siguiente atributo al botón “Salir” en el layout activity_main.xml:

android:onClick="salir"

Preguntas de repaso: Actividades

Comunicación entre actividades

Cuando una actividad ha de lanzar a otra actividad en muchos casos necesita enviarle cierta información.

video[Tutorial] Intercambio de datos entre actividades

Android nos permite este intercambio de datos utilizando el mecanismo que es descrito a continuación:

Cuando lances una actividad usa el siguiente código:

Intent intent = new Intent(this, MI_CLASE.class);
intent.putExtra("usuario", "Pepito Perez");
intent.putExtra("edad", 27);
startActivity(intent);

En la actividad lanzada podemos recoger los datos de la siguiente forma:

Bundle extras = getIntent().getExtras();
String s = extras.getString("usuario");
int i = extras.getInt("edad");

Cuando la actividad lanzada termina también podrá devolver datos que podrán ser recogidos por la actividad lanzadora de la siguiente manera.

Intent intent = new Intent(this, MI_CLASE.class);
startActivityForResult(intent, 1234);
 ...
@Override protected void onActivityResult (int requestCode, 
                                  int resultCode, Intent data){
    if (requestCode==1234 && resultCode==RESULT_OK) {
       String res = data.getExtras().getString("resultado");
    }
}

En la actividad llamada has de escribir:

Intent intent = new Intent();
intent.putExtra("resultado","valor");
setResult(RESULT_OK, intent);
finish();

Práctica: Comunicación entre actividades.

1.     Crea un nuevo proyecto con nombre ComunicacionActividades.

2.     El Layout de la actividad inicial ha de ser similar al que se muestra abajo a la izquierda.

3.   Introduce el código para que cuando se pulse el botón “Verificar” se arranque una segunda actividad. A esta actividad se le pasará como parámetro el nombre introducido en el EditText.

4.     El Layout correspondiente a la segunda actividad se muestra a la derecha.

5. Al arrancar la actividad el texto del primer TextView ha de modificarse para que ponga “Hola ”+nombre recibido+”¿Aceptas las condiciones?”

6.    En esta actividad se podrán pulsar dos botones, de forma que se devuelva a la actividad principal el String “Aceptado” o “Rechazado”, según el botón pulsado. Al pulsar cualquier botón se regresará a la actividad anterior.

7.  En la actividad principal se modificará el texto del último TextView para que ponga “Resultado: Aceptado” o “Resultado: Rechazado”, según lo recibido.

Preguntas de repaso: Comunicación entre Actividades

Añadiendo un menú a una actividad

Android permite asignar menús a las actividades, que se despliegan cuando se pulsa la tecla de menú. Este tipo de menús resultan muy interesantes en dispositivos con pantallas pequeñas dado que no ocupan parte de la pantalla, es decir, están ocultos hasta que se pulsa la tecla de menú.

Desde la versión 3.0 ya no es obligatorio que un dispositivo Android disponga de la tecla de menú. No obstante, algunos dispositivos, como los de la marca Samsung, siguen incorporando esta tecla. En la versión 3.0 aparece la barra de acciones, donde se integra el menú de las actividades. Este elemento, que tiene una gran importancia en el diseño de la interfaz de usuario en una aplicación Android, se estudiará en el siguiente punto. En este punto estudiaremos el planteamiento anterior a la aparición de la barra de acciones, es decir, el menú de actividad.

Aunque este tipo de menús pueda parecer un elemento obsoleto en una aplicación actual, resulta interesante estudiarlo primero. A continuación se justifica por qué:

  • En la actualidad algunos usuarios utilizan terminales con una versión anterior a la 3.0. En estos casos los menús de serán mostrados como se aparecen en este punto.
  • En algunas actividades no querremos que se muestre la barra de acciones y preferimos utilizar este tipo de menús ocultos. Por ejemplo, si queremos tener toda la pantalla para la actividad o si la estética que buscamos no combina con la barra de acciones.
  •  La barra de acciones es una evolución compatible con los menús de actividad. Estos dos elementos resultan más fáciles de entender si seguimos el orden en que han aparecido.

NOTA: En el siguiente vídeo se habla de”menús contextuales”. Este termino no es  adecuado dado que puede confundirse con otro tipo de menús. Un termino más preciso sería “menú de actividad”.

video[Tutorial Añadiendo un menú en Android

Ejercicio: Añadiendo un menú a una actividad

 Podemos asignar un menú a nuestra actividad de forma muy sencilla.

1.    Abre el proyecto Asteroides o Mislugares (según la aplicación que realices).

2.  Para crear un nuevo menú, usa File > New > Android resource file. En el campo File name: selecciona menu_main y en el campo Resource type: selecciona Menu.

NOTA: Es posible que este fichero de menú ya esté creado. Esto ocurre cuando se crea un nuevo proyecto  seleccina Add an activity: Basic Activity o Scrolling Activity.

3.    Reemplaza el contenido que se muestran a continuación:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/ tools"
      tools:context=".MainActivity">
   <item android:id="@+id/action_settings"
         android:title="Preferencias"
         android:icon="@android:drawable/ic_menu_preferences"
         android:orderInCategory="100"
         app:showAsAction="never"/>
   <item android:title="Acerca de..."
         android:id="@+id/acercaDe"
         android:icon="@android:drawable/ic_menu_info_details"/>
</menu>
Como puedes ver cada ítem de menú tiene tres atributos principales: id que permite identificarlo desde el código, title, para asociarle un texto e icon, para asociarle un icono. Los otros dos atributos se aplican cuando el menú actua como una barra de acciones y serán explicados en el próximo punto. 

4.    A continuación se muestra la apariencia de este menú. Esta apariencia puede cambiar dependiendo de la versión de Android.

5.      Para activar el menú, has de introducir el siguiente código en MainActivity.java. Posiblemente solo tengas que incluir el texto subrayado.

@Override public boolean onCreateOptionsMenu(Menu menu) {
   getMenuInflater().inflate(R.menu.menu_main, menu);
   return true; /** true -> el menú ya está visible */
}
    
@Override public boolean onOptionsItemSelected(MenuItem item) {
   int id = item.getItemId();
   if (id == R.id.action_settings) {
      return true;
   }
   if (id == R.id. acercaDe) {
      lanzarAcercaDe(null);
      return true;
   }
   return super.onOptionsItemSelected(item);
}

6.     Ejecuta el programa y pulsa la tecla del menú del terminal o el botón  correspondiente en pantalla. Han de aparecer los dos ítems de menú. Selecciona Acerca de… para pasar a la actividad correspondiente.

Preguntas de repaso: Menús

La barra de acciones (ActionBar)

video[Tutorial La barra de acciones (ActionBar)

Desde la versión 3.0, se introdujo en Android un nuevo elemento en la interfaz de usuario: la barra de acciones o ActionBar. Situada en la parte superior de la pantalla, fue creada para que el usuario tuviera una experiencia unificada a través de las distintas aplicaciones. La barra de acciones aglutina varios elementos; los más habituales son el icono de la aplicación con su nombre y los botones de acciones frecuentes. Las acciones menos utilizadas se sitúan en un menú desplegable, que se abrirá desde el botón Overflow. Si la aplicación dispone de pestañas (tabs), estas podrán situarse en la barra de acciones. También pueden añadirse otros elementos, como listas desplegables y otros tipos de widgets incrustados, como el widget de búsqueda que veremos más adelante.

En caso de disponer de menos tamaño de pantalla el sistema puede redistribuir los elementos y pasar alguna acción al menú de «Overflow». Por ejemplo, en un móvil la barra de acciones anterior se podría ver de la siguiente manera:

Los dispositivos anteriores a la versión 3.0 requerían una tecla física para mostrar el menú de la actividad. Sin embargo, con esta versión dicha tecla deja de ser un requisito de los terminales y los menús pasan a mostrarse en la barra de acciones. En los dispositivos que sí que dispongan de este botón físico, es posible que los tres puntos que representan el menú de Overflow no se representen en la barra de acciones. En este caso tienes que pulsar el botón físico para desplegar este menú.

La barra de acciones se configura igual que los menús disponibles desde la primera versión. Es decir, a través de ficheros XML de menús, almacenados en res/menu. Esto permite diseñar el menú de una aplicación de forma convencional. Cuando la aplicación se ejecute en una versión inferior a la 3.0, el menú se mostrará de forma tradicional. Cuando el usuario pulse la tecla de menú, aparecerá:

Pero, en caso de disponer de una versión 3.0 o superior, se mostrará en la barra de acciones.

Añadir una ActionBar a la aplicación es muy sencillo. Es más, gracias a que se utiliza la misma herramienta que para mostrar menús en dispositivos con versiones anteriores, todas las opciones de la ActionBar estarán disponibles independientemente de la versión que se esté utilizando. Por lo tanto, no hay que realizar ningún paso para que se visualice la barra de acciones. Todos los temas de Android a partir de la versión 3.0 incorporan por defecto la barra de acciones visible (menos los que acaban en NoActionBar). Si se desea ocultar la barra de acciones desde código, es muy sencillo; pero mostrarla una vez aplicado un estilo que la deshabilite es imposible.

Desde finales de 2013 también podemos utilizar la ActionBar en versionesanteriores a la 3.0 gracias a la librería de compatibilidad appcompat-v7. Para añadir la barra de acciones usando esta librería has de seguir los siguientes pasos:

1.     Asegúrate de que en tu proyecto esté la librería appcompat-v7. Suele ser así, dado que las herramientas actuales de SDK la incorporan de forma automática al crear un nuevo proyecto.

2.     Haz que tu actividad herede de ActionBarActivity o AppCompatActivity en lugar de Activity.

3.     En AndroidManifest has de aplicar un tema adecuado a la actividad o a toda la aplicación. Estos temas han de pertenecer a Theme.AppCompat.* o ser descendientes de estos. Cuando creamos un nuevo proyecto, se define como tema para toda la aplicación @style/AppTheme. Si vas a su definición en el fichero styles.xml, puedes observar que es descendiente de Theme.AppCompat.Light.DarkActionBar. Por lo tanto, ya se está utilizando un tema adecuado.

Veremos cómo realizarlo en uno de los siguientes ejercicios.

Ejercicio: Añadiendo una barra de acciones a nuestra aplicación.

1.    Reemplaza el contenido del fichero res/menu/menu_main.xml por:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/ tools"
      tools:context=".MainActivity">
   <item android:id="@+id/action_settings"
         android:title="Configuración"
         android:icon="@android:drawable/ic_menu_preferences"
         android:orderInCategory="5"
         app:showAsAction="never"/>
   <item android:title="Acerca de..."
         android:id="@+id/acercaDe"
         android:icon="@android:drawable/ic_menu_info_details"
         android:orderInCategory="10"
         app:showAsAction="ifRoom|withText"/>
   <item android:title="Buscar"
         android:id="@+id/menu_buscar"
         android:icon="@android:drawable/ic_menu_search"
         android:orderInCategory="115"
         app:showAsAction="always|collapseActionView"/>
</menu>

Este fichero XML es el que define los iconos y acciones a mostrar. Como ya hemos dicho, para los dispositivos con una versión 3.0 o superior, este menú se muestra en la barra de acciones. Las acciones que indiquen en el atributo showAsAction la palabra always serán mostrados siempre, sin importar si caben o no. El uso de estas acciones debería limitarse a unas pocas, o incluso mejor si no hay ninguna, ya que al forzar que se visualicen todas al mismo tiempo podrían verse incorrectamente. Las acciones que indiquen ifRoom serán mostradas en la barra de acciones si hay espacio disponible, y serán movidas al menú de Overflow  si no lo hay. En esta categoría se deberían encontrar la mayoría de acciones. Si se indica never la acción nunca se mostrará en la barra de acciones, sin importar el espacio disponible. En este grupo se deberían situar acciones como modificar las preferencias, que deben estar disponibles al usuario; pero no visibles en todo momento.

Las acciones se ordenan de izquierda a derecha según lo indicado en orderInCategory, con las acciones con un número más pequeño más a la izquierda. Si todas las acciones no caben en la barra, las que tienen un número menor son movidas al menú de Overflow.

2.    Ejecuta la aplicación. Podrás ver como aparece la barra de acciones en la parte de arriba, con los botones que hemos definido.

 

3.    Si prefieres que en una actividad no se muestre la barra de acciones, puedes asignarle un tema terminado en .NoTitleBar. Para ello edita el fichero AndroidManifest.xml y añade la línea subrayada.

<activity android:name="com.example.asteroides.MainActivity"
          android:label="@string/app_name"
          android:theme="@android:style/Theme.NoTitleBar">  

4.    Ejecuta de nuevo el programa y observa como ya no se muestra la barra de acciones.

5.    Reemplaza ahora el tema de la actividad por el mostrado a continuación:

android:theme="@android:style/Theme.NoTitleBar.Fullscreen">

6.    Observa como también se elimina la barra de estado de Android.

La nueva versión de Android Studio 2.2 incorpora un editor visual de menús que nos permite crear menús sin necesidad de escribir código xml.

 

 

Creación y uso de iconos

 

En el apartado anterior hemos creado una barra de acciones donde se mostraban algunos iconos. Se han utilizado iconos disponibles en el sistema Android, es decir, recursos ya almacenados en el sistema. Otra alternativa es crear tus propios iconos y almacenarlos en la carpeta  res/mipmap. En este apartado aprenderemos a hacerlo.

video[Tutorial Añadiendo iconos en Android

En Android se utilizan diferentes tipos de iconos según su utilidad. La siguiente tabla muestra los más importantes:

Tipo de iconos

Finalidad

Ejemplos

Lanzadores

Representa la aplicación en la pantalla principal del dispositivo

Barra de acciones

Opciones disponibles en la barra de acciones

 
          

Notificaciones

Pequeños iconos que aparecen en la barra de estado (véase capítulo 8)

Otros

También es muy frecuente el uso de iconos en cuadros de dialogo y en RecyclerView, etc.

A la hora de  diseñar tus propios iconos, has de tener en cuenta algunos aspectos. En primer lugar, recuerda que Android ha sido concebido para ser utilizado en dispositivos con una gran variedad de densidades gráficas. Este rango puede ir desde 100 píxeles/pulgada (ldpi) hasta 340 píxeles/pulgada (xhdpi). Por lo tanto, vas a tener que crear tus iconos en varias densidades gráficas.

Resulta interesante que plantees tus diseños con una resolución como mínimo de 300 píxeles/pulgada. Luego puedes usar las herramientas del sistema para obtener estos icones en diferentes densidades. Si para alguna densidad el resultado no es el adecuado tendrás que realizar algunos retoques (en el siguiente ejercicio se muestra un ejemplo).

En segundo lugar, tu aplicación se ejecutará dentro de un sistema donde se utilizan ciertas guías de estilo. Si quieres que tus iconos no desentonen, lee la documentación que se indica a continuación.

Enlaces de interés:

Guía de estilo para iconos:La siguiente página describe las guías de estilo para los iconos en Material Design:

        https://material.google.com/style/icons.html

Recursos de iconos:En las siguientes páginas puedes encontrar gran variedad de iconos:

https://materialdesignicons.com

https://design.google.com/icons/#ic_list

https://android-material-icon-generator.bitdroid.de/

Ejercicio: Creación de iconos personalizados

Veamos un ejemplo práctico de como crear un icono: El diseñador gráfico de nuestra empresa nos acaba de pasar el icono asociado para iniciar la aplicación que estamos diseñando (launcher icon).


(Pulsa con el botón derecho sobre el gráfico anterior para descargarlo)

Para asignarlo a una aplicación realiza los siguientes pasos:

1. Descarga el gráfico anterior  y llámale icon.png.

2. En el explorador proyecto pulsa con el botón derecho sobre la carpeta res y selecciona  File / New / Image Assets.

3.  En el campo Name: introduce ic_mi_icono; en Asser Type: Image y en Path: indica el fichero descargado en el paso 1. El resultado ha de ser similar a:

En la parte inferior de la página podrás previsualizar cómo quedarán las imágenes para diferentes densidades gráficas.

4.     Pulsa el botón Next y en la siguiente pantalla pulsa Finish.

5.     Verifica como dentro de la carpeta res/mipmap se han creado 5 recursos alternativos para diferentes densidades gráficas:

 

6.    El resultado es aceptable para algunas opciones. Pero en algunos casos el texto no se lee o unas líneas horizontales quedan más gruesas que otras. Puede ser interesante retocar estos fichero png usando un editor de gráficos. Por ejemplo, retoca los iconos hdpi y mdpi para que se pueda leer más claramente el texto “Aplicación” y redibuja las líneas horizontales para que tenga el mismo grosor y separación.

7.    Para que el nuevo icono sea utilizado como lanzador de nuestra aplicación, abre AndroidManifest.xml y reemplaza el valor del atributo iconcomo se muestra a continuación:

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher mi_icono"

8. Visualiza el resultado instalando la aplicación en diferentes dispositivos con diferentes densidades gráficas.

Práctica: Creación de iconos para la aplicación

1.     Dibuja o busca en Internet un gráfico que sea adecuado para usar como icono de inicio en la aplicación.

2.     Repite los pasos indicados en el ejercicio anterior para crear los iconos en las diferentes densidades gráficas.

En caso de haber realizado la práctica anterior, puedes hacer el siguiente ejercicio:

Ejercicio: Creación de iconos para la aplicación Mis Lugares

1.     Descarga el siguiente fichero:

http://www.dcomg.upv.es/~jtomas/android/ficheros/mislugares_iconos.zip

2.     Utiliza los iconos que contiene para asociarlos a la aplicación Mis Lugares.

Preguntas de repaso: Iconos

 

Añadiendo preferencias de usuario


Android nos facilita la configuración de nuestros programas, al permitir añadir una lista de preferencias que el usuario podrá modificar. Por ejemplo, el usuario podrá indicar con que frecuencia la aplicación ha de sincronizarse con el servidor o si queremos que se lancen notificaciones. Las preferencias también pueden utilizarse para que tu aplicación almacene información de forma permanente. En el capítulo 9 se estudiará cómo realizar esta función

video[Tutorial Añadir preferencias en Android

Nota: A continuación se proponen una serie de ejercicios para añadir preferencias en Asteroides. Para hacerlo en Mis Lugares utiliza el tutorial:   http://www.androidcurso.com/index.php/476.

 

Ejercicio: Añadiendo preferencias a Asteroides

1.     Abre el proyecto Asteroides.

2.   Pulsa con el botón derecho sobre la carpeta res y selecciona la opción New > Android resource file.

3.   Completa los campos File name: preferenciasyResource type: XML. Se creará el fichero res/xml/preferencias.xml.

4.     Edita el fichero e introduce el siguiente código:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:key="preferencias_principal" >
    <CheckBoxPreference
        android:key="musica"
        android:title="Reproducir música"
        android:summary="Se reproduce música de fondo"/>
    <ListPreference
        android:key="graficos"
        android:title="Tipo de gráficos"
        android:summary="Se escoge la representación de gráficos"
        android:entries="@array/tiposGraficos"
        android:entryValues="@array/tiposGraficosValores"
        android:defaultValue="1"/>
    <EditTextPreference
        android:key="fragmentos"
        android:title="Número de Fragmentos"
        android:summary="En cuantos trozos se divide un asteroide"
        android:inputType="number"
        android:defaultValue="3"/>
</PreferenceScreen>

El significado de cada etiqueta y atributo se descubre facilmente si observas el resultado obtenido que se muestra a continuación. El atributo inputTypepermite configurar el tipo de teclado que se mostrará para introducir el valor. Coinciden con el atributo de EditText. Para ver los posibles valores consultar developer.android.com/reference/android/widget/TextView.html#attr_android:inputType.

 

5.     Para almacenar los valores del desplegable, has de crear el fichero /res/values/arrays.xml con el siguiente contenido.  Para ello pulsa con el botón derecho sobre la carpeta res y selecciona la opciónNew > Android resource file. Completa los camposFile name: arrays yResource type: values.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="tiposGraficos">
        <item>vectorial</item>
        <item>bitmap</item>
        <item>3D</item>
    </string-array>
    <string-array name="tiposGraficosValores">
        <item>0</item>
        <item>1</item>
        <item>2</item>
    </string-array>
</resources>

6.     Crea ahora una nueva clase Preferencias.java con el siguiente código:

public class Preferencias extends PreferenceActivity {
   @Override protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      addPreferencesFromResource(R.xml.preferencias);
   }
}

Nota sobre Java: Pulsa Alt-Intro para que automáticamente se añadan los paquetes que faltan.

NOTA: Desde el nivel de API 11, el método addPreferencesFromResource() se ha marcado como obsoleto. En lugar de usar la clase PreferenceActivity, se recomienda utilizar PreferenceFragment. Sin embargo, si estamos trabajando con un nivel mínimo de API inferior al 11 (en nuestro caso el 9), no disponemos de esta clase. Cuando un método o clase está marcado como obsoleto, se nos indica que hay otra forma más moderna de hacerlo, incluso que en un futuro es posible que deje de estar incluido en el API. Pero esto es algo que nunca ha pasado. En Android es frecuente seguir utilizando métodos marcados como obsoletos. En el siguiente apartado se explica el uso de PreferenceFragment.

NOTA: Recientemente, Google ha creado las librerías compatibilidad preference-v7 y preference-v14 donde se define PreferenceFragmentCompact una versión de PreferenceFragment compatible con versiones anteriores. El uso de PreferenceFragmentCompact se muestra en el Gran Libro de Android Avanzado.

7.     No hay que olvidar registrar toda nueva actividad en AndroidManifest.xml.

8.  Añade a MainActivity.java el método lanzarPreferencias(). Este método ha de tener el mismo código que lanzarAcercaDe()pero lanzando la actividad Preferencias. En el Layout activity_main.xml añade al botón con texto “Configurar” en el atributo onClick el valor lanzarPreferencias.

9.  Para activar la configuración desde la opción de menú añade el siguiente código en el fichero MainActivity.java en el método onOptionsItemSelected()

if  (id == R.id.action_settings) {
       lanzarPreferencias(null);
       return true;
  }

10.  Arranca la aplicación y verifica que puedes lanzar las preferencias mediante las dos alternativas.

Utilizando PreferenceFragment

Cuando tu aplicación utilice un nivel de API 11 o superior (versión 3.0), puedes utilizar PreferenceFragment en lugar de PreferenceActivity para visualizar la lista de preferencias. La ventaja de PreferenceFragment es que muestra las preferencias en un fragment en lugar de una actividad, por lo que podrás visualizar simultáneamente en pantalla un fragment con las preferencias junto a otro fragment. Esto tendría sentido en una tableta, pero no sería interesante en un móvil. En una primera toma de contacto con Android no recomendamos utilizar fragments. Si es tu caso, te recomendamos que pases al siguiente apartado. Si prefieres utilizar PreferenceFragment puedes realizar el siguiente ejercicio.

Ejercicio: Añadiendo preferencias a con PreferenceFragment.

1.     El primer paso va a ser modificar la versión mínima de API utilizada en el proyecto Asteroides. Hemos de pasar de 9 a 11. Esto va a tener una importante repercusión en la aplicación, dado que ahora solo podrá ser instalada en dispositivos Android con versión 3.0 o superior. A finales de 2015 todavía hay algunos dispositivos con versión 2.3 (5 %), donde ya no se podrá instalar la aplicación.

Para cambiar la versión mínima, desde el navegador del proyecto, abre el fichero Gradle Scripts / build.gradle (Module:app) y modifícalo eliminando el código tachado e introduciendo el subrayado:

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"
    defaultConfig {
        applicationId "org.example.asteroides"
        minSdkVersion 9 11
        targetSdkVersion 22
        …
    }
…

2.     Ahora vamos a crear un fragment que contenga las preferencias. Para ello añade la siguiente clase en el paquete de la aplicación:

public class PreferenciasFragment extends PreferenceFragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferencias);
    }
}

Como puedes ver el código es idéntico al utilizado en PreferenceActivity.

3.     Ahora desde la Actividad Preferencias vamos a mostrar este fragment. Para ello elimina el código tachado e introduce el subrayado:

public class Preferencias extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferencias);
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new PreferenciasFragment())
                .commit();
    }
}

Desde una actividad podemos visualizar un fragment en tiempo de ejecución. Para ello utilizamos el manejador de fragments de la activdad (getFragmentManager()) y comenzamos una transacción (beginTransaction()). Una transacción es una operación de insertado, borrado o reemplazo de fragments. En el ejemplo vamos a reemplazar el contenido de la actividad por un nuevo fragment de la clase PreferenciasFragment. Finalmente se llama a commit() para que se ejecute la transacción.

4.     Ejecuta la aplicación y verifica que el resultado es idéntico al anterior.

Organizando preferencias

Cuando el número de preferencias es grande, resulta interesante organizarlas de forma adecuada. Una posibilidad consiste en dividirlas en varias pantallas, de forma que cuando se seleccione una opción en la primera pantalla, se abra una nueva pantalla de preferencias. Para organizar las preferencias de esta forma, usa el siguiente esquema:

<PreferenceScreen>
  <CheckBoxPreference …/>
  <EditTextPreference …/>
  …
  <PreferenceScreen android:title=”Modo multijugador”> 
    <CheckBoxPreference …/> 
    <EditTextPreference …/>

    …
  </PreferenceScreen> 
</PreferenceScreen>

Práctica: Organizando preferencias(I)

1. Crea una nueva lista de preferencias <PreferenceScreen> dentro de la lista de preferencias del fichero res/xml/preferencias.xml.

2. Asígnale al parámetro android:title el valor “Modo multijugador”.

3. Crea tres elementos dentro de esta lista: Activar multijugador, Máximo de jugadores y Tipo de conexión. Para este ultimo han de poder escogerse los valores: Bluetooth, Wi-Fi e Internet.

Otra alternativa para organizar las preferencias consiste en agruparlas por categorías. Con esta opción se visualizarán en la misma pantalla, pero separadas por grupos. Has de seguir el siguiente esquema:

<PreferenceScreen>
  <CheckBoxPreference …/>
  <EditTextPreference …/>
  …
  <PreferenceCategory android:title=”Modo multijugador”>
    <CheckBoxPreference …/>
    <EditTextPreference …/>
.  . …
  </PreferenceCategory>
</PreferenceScreen>

A continuación se representa la forma en la que Android muestra las categorías:

Práctica: Organizando preferencias(II)

Modifica la práctica anterior para que en lugar de mostrar las propiedades en dos pantallas, las muestre en una sola, tal y como se muestra en la imagen anterior.

Como se almacenan las preferencias de usuario

Si un usuario modifica el valor de una preferencia, este quedará almacenado de forma permanente en el dispositivo. Para conseguir esta persistencia, Android almacena las preferencias seleccionadas en un fichero XML dentro de la carpeta data/data/nombre.del.paquete/files/shared_prefs, donde nombre.del.paquete ha de ser reemplazado por el paquete de la aplicación. El nombre del fichero para almacenar las preferencias de usuario ha de ser siempre nombre.del.paquete_preferences.xml. Esto significa que solo puede haber unas preferencias de usuario por aplicación. Como se estudiará en el capítulo 9, puede haber otros ficheros de preferencias; pero, a diferencia de las preferencias de usuario, no pueden ser editadas directamente por el usuario, sino que hay que acceder a ellas por código.

Ejercicio: Donde se almacenan las preferencias de usuario

Veamos donde se han almacenado las preferencias que acabamos de crear:

1.    Para navegar por el sistema de ficheros de un dispositivo con Android Studio, abre la opción Tools > Android > Android Device Monitor y selecciona la lengüeta File Explorer.

2.    Busca el siguiente fichero:  /data/data/org.examples.asteroides/shared_prefs/ org.examples.asteroide_preferences.xml

NOTA: En un dispositivo real no tendrás permiso para acceder a estos ficheros (A menos que el dispositivo haya sido rooteado).

3. Pulsa el botón del disquete para descargar el fichero en tu PC.

4. Visualiza su contenido. Tiene que ser similar a:

<map>
   <boolean name="musica"value="true" />
   <string name="graficos">1</string> 
   <string name="fragmentos">3</string>
</map>

Accediendo a los valores de las preferencias

Por supuesto, será necesario acceder a los valores de las preferencias para alterar el funcionamiento de nuestra aplicación. El siguiente ejemplo nos muestra cómo realizarlo:

public void mostrarPreferencias(){
   SharedPreferences pref =
               PreferenceManager.getDefaultSharedPreferences(this);
   String s = "música: "+ pref.getBoolean("musica",true)
            +", gráficos: " + pref.getString("graficos","?");
   Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
}

La función comienza creando el objeto pref de la clase SharedPreferences y le asigna las preferencias definidas para la aplicación. A continuación crea el String s y le asigna los valores de dos de las  preferencias. Se utilizan los métodos pref.getBoolean() y pref.getString(), que disponen de dos parámetros: el valor dekeyque queremos buscar ("musica" y "graficos") y el valor asignado por defecto en caso de no encontrar esta key.

Finalmente se visualiza el resultado utilizando la clase Toast. Los tres parámetros indicados son el contexto (nuestra actividad),  el String a mostrar y el tiempo que se estará mostrando esta información.

Ejercicio: Accediendo a los valores de las preferencias

1. Copia la función anterior en la clase Asteroides. Añade el parámetro que se muestra a continuación  mostrarPreferencias(View view).

2. Asígnala al atributo onClick del botón Jugar el método anterior.

3. Visualiza también el resto de las preferencias que hayas introducido.

Verificar valores correctos

En muchas ocasiones vas a querer limitar los valores que un usuario puede introducir en las preferencias. Por ejemplo, podría ser interesante que el valor introducido por el usuario en la preferencia número de fragmentos solo pudiera tomar valores entre 0 y 9. Para conseguir esto podemos utilizar el escuchador de evento onPreferenceChangeListener que podremos asignar a una preferencia. Veamos cómo actuar en el siguiente ejercicio:

Ejercicio: Verificar valores correctos de una preferencia

1.     Copia el siguiente código al final del método onCreate():

final EditTextPreference fragmentos = (EditTextPreference)findPreference(
                                                           "fragmentos");
fragmentos.setOnPreferenceChangeListener(
  new Preference.OnPreferenceChangeListener() {
    @Override
    public boolean onPreferenceChange(Preference preference, Object
                                                              newValue) {
       int valor;
       try {
          valor = Integer.parseInt((String)newValue);
       } catch(Exception e) {
          Toast.makeText(getActivity(), "Ha de ser un número",
                                              Toast.LENGTH_SHORT).show();
          return false;
       }
       if (valor>=0 && valor<=9) {
          fragmentos.setSummary(
                 "En cuantos trozos se divide un asteroide ("+valor+")");
          return true;
       } else {
          Toast.makeText(getActivity(), "Máximo de fragmentos 9",
                                              Toast.LENGTH_SHORT).show();
          return false;
       }
    }
  });

El código comienza obteniendo una referencia de la preferencia fragmentos, para asignarle un escuchador que será llamado cuando cambie su valor. El escuchador comienza convirtiendo el valor introducido a entero. En caso de producirse un error es por que el usuario no ha introducido un valor adecuado. En este caso, mostramos un mensaje y devolvemos false para que el valor de la preferencia no sea modificado. Si no hay error, tras verificar el rango de valores aceptables, modificamos la explicación de la preferencia para que aparezca el nuevo valor entre paréntesis y devolvemos true para aceptar este valor. Si no está en el rango, mostramos un mensaje indicando el problema y devolvemos false.

2.     Ejecuta el proyecto y verifica que funciona correctamente.

Práctica: Mostrar el valor de una preferencia

En el ejercicio anterior cuando se modifica el número de fragmentos se muestra entre paréntesis el nuevo valor introducido. El funcionamiento no es del todo correcto, cuando entramos por primera vez o cuando se cambia el teléfono de vertical a horizontal este valor no se muestra. Añade el código necesario para que este valor aparezca siempre.

 

Preguntas de repaso: menús y preferencias

Creando actividades en Mis Lugares

La actividad VistaLugarActivity nos mostrará la información que hemos almacenado de un determinado lugar y nos permitirá realizar una gran cantidad de acciones sobre ese lugar (mostrar en mapa, llamar por teléfono, compartir en redes sociales, etc.). Desde esta actividad podremos cambiar algunos valores de modificación frecuente. En concreto: la valoración, la fecha de visita y la fotografía.

Ejercicio: Creación de Ia actividad VistaLugar.

1.     Abre el proyecto MisLugares.

2.     Descarga  http://www.dcomg.upv.es/~jtomas/android/ficheros/mislugares.zip y descomprime en una carpeta. Copia los gráficos que encontrarás en el portapapeles y pegalos dentro de res/drawable en el explorador del proyecto.

3.    Crea un nuevo layout y llámalo vista_lugar.xml. Copia el siguiente código para usarlo como base:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scrollView1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >
        <TextView
            android:id="@+id/nombre"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:gravity="center"
            android:text="Nombres del lugar"
            android:textAppearance="?android:attr/textAppearanceLarge" />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >
            <ImageView
                android:id="@+id/logo_tipo"
                android:layout_width="40dp"
                android:layout_height="40dp"
                android:contentDescription="logo del tipo"
                android:src="@drawable/otros" />
            <TextView
                android:id="@+id/tipo"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="bottom"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:text="tipo del lugar" />
        </LinearLayout>
      …
        <RatingBar
            android:id="@+id/valoracion"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_toRightOf="@+id/foto"
            android:rating="3" />
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
            <ImageView
                android:id="@+id/foto"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:adjustViewBounds="true"
                android:contentDescription="fotografía"
                android:src="@drawable/foto_epsg" />
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="right" >
            <ImageView
                android:layout_width="40dp"
                android:layout_height="40dp"
                android:contentDescription="logo cámara"
                android:src="@android:drawable/ic_menu_camera" />
            <ImageView
                android:layout_width="40dp"
                android:layout_height="40dp"
                android:contentDescription="logo galería"
                android:src="@android:drawable/ic_menu_gallery" />
            </LinearLayout>
        </FrameLayout>
    </LinearLayout>
</ScrollView>

Observa como el elemento exterior es un ScrollView. Esto es conveniente cuando pensemos que los elementos de layout no cabrán en la pantalla. En este caso el usuario podrá desplazar verticalmente el layout arrastrando con el dedo. Dado que algunas pantallas pueden ser muy pequeñas la mayoría de diseños han de incorporar un ScrollView.  

Dentro de este elemento tenemos un LinearLayout para organizar las vistas verticalmente. La primera vista es un TextView cuyo id es nombre. Se ha asignado un valor para text inicial, que será reemplazado por el nombre del lugar. La única función que tiene este texto inicial es ayudarnos en el diseño. El siguiente elemento es un LinearLayout vertical que contiene un ImageView y un TextView. Este elemento será utilizado para indicar el tipo de lugar.

Los puntos suspensivos indican el lugar donde tendrás que insertar el resto de elementos que no se han incluido (dirección, teléfono, etc.). El siguiente elemento que se incluye es un RatingBar donde podremos introducir una valoración del lugar. El último elemento es un FrameLayout que permite superponer varias vistas. Se dibujarán en el orden en que las indicamos. En el fondo se dibuja un ImageView con una fotografía de la EPSG. El atributo adjustViewBounds indica que la imagen sea escalada para ocupar todo el espacio disponible. Sobre la fotografía se dibujará un LinearLayout con dos ImageView. Estos botones permitirán cambiar la fotografía desde la cámara o desde la galería.

4.     Reemplaza los puntos suspensivos por los elementos que faltan para obtener la apariencia mostrada al principio de este punto. Para cada elemento haz una copia del <LinearLayout> donde se introduce el tipo de lugar y reemplaza los valores que consideres necesarios. Utiliza los recursos del sistema mostrados en la siguiente tabla. Identifica dada TextView con el id que se indica.

Recurso para ImageView

Id para TextView

@android:drawable/ic_menu_myplaces

@+id/direccion

@android:drawable/ic_menu_call

@+id/telefono

@android:drawable/ic_menu_mapmode

@+id/url

@android:drawable/ic_menu_info_details

@+id/comentario

@android:drawable/ic_menu_my_calendar

@+id/fecha

@android:drawable/ic_menu_recent_history

@+id/hora

 

5.     Crea la clase VistaLugarActivity y reemplaza el código por el siguiente:

public class VistaLugarActivity extends AppCompatActivity {
    private long id;
    private Lugar lugar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.vista_lugar);
        Bundle extras = getIntent().getExtras();
        id = extras.getLong("id", -1);
        lugar = MainActivity.lugares.elemento((int) id);
        TextView nombre = (TextView) findViewById(R.id.nombre);
        nombre.setText(lugar.getNombre());
        ImageView logo_tipo = (ImageView) findViewById(R.id.logo_tipo);
        logo_tipo.setImageResource(lugar.getTipo().getRecurso());
        TextView tipo = (TextView) findViewById(R.id.tipo);
        tipo.setText(lugar.getTipo().getTexto());
        TextView direccion = (TextView) findViewById(R.id.direccion);
        direccion.setText(lugar.getDireccion());
        TextView telefono = (TextView) findViewById(R.id.telefono);
        telefono.setText(Integer.toString(lugar.getTelefono()));
        TextView url = (TextView) findViewById(R.id.url);
        url.setText(lugar.getUrl());
        TextView comentario = (TextView) findViewById(R.id.comentario);
        comentario.setText(lugar.getComentario());
        TextView fecha = (TextView) findViewById(R.id.fecha);
        fecha.setText(DateFormat.getDateInstance().format(
            new Date(lugar.getFecha())));
        TextView hora = (TextView) findViewById(R.id.hora);
        hora.setText(DateFormat.getTimeInstance().format(
            new Date(lugar.getFecha())));
        RatingBar valoracion = (RatingBar) findViewById(R.id.valoracion);
        valoracion.setRating(lugar.getValoracion());
        valoracion.setOnRatingBarChangeListener(
            new OnRatingBarChangeListener() {
                @Override public void onRatingChanged(RatingBar ratingBar,
                                         float valor, boolean fromUser) {
                    lugar.setValoracion(valor);
                }
        });
    }
}

Nota sobre Java: Pulsa Alt-Intro  para que automáticamente se añadan los imports con los  paquetes que faltan. Dos clases aparecen en varios paquetes, selecciona java.text.DateFormat y java.util.Date.

El método onCreate() será ejecutado cuando se cree la actividad y en el tenemos que asociar un layout (setContentView(R.layout.vista_lugar)) e inicializar todos sus valores. Lo primero que se hace es averiguar el id del lugar a mostrar, que ha sido pasado en un extra. A partir de este id obtenemos el objeto Lugar a mostrar. Tanto este objeto como el id son almacenados en variables globales para que puedan ser accedidas desde cualquier método de la actividad.

Observa cómo se obtiene un objeto de cada uno de los elementos de la vista utilizando el método findViewById(). A continuación este objeto es modificado según el valor del lugar que estamos representando. Al final se realiza una acción especial con el objeto valoracion, utilizando el método setOnRatingBarChangeListener() para asignarle un escuchador de eventos al RatingBar que es creado allí mismo. Este escuchador de evento se activará cuando el usuario modifique la valoración. El código a ejecutar consiste en llamar  al método setValoracion() del objeto lugar con la nueva valoración.

6.     En la primera unidad se creó el proyecto Mis LugaresJava. Abre este proyecto y selecciona las clases GeoPunto, Lugar, Lugares, LugaresVector y TipoLugar (Puedes seleccionar varios ficheros manteniendo la tecla Ctrl pulsada). Con el botón derecho selecciona la opción Copy. Con el botón derecho pulsa sobre org.example.mislugares del proyecto Mis Lugares y selecciona la opción Paste.

7.     Abre la clase TipoLugar y asigna un recurso drawable a cada tipo de lugar. Puedes utilizar la opción de autocompletar, es decir, escribe R.drawable. y espera a que el sistema te dé una alternativa.

public enum TipoLugar {
    OTROS ("Otros", R.drawable.otros),
    RESTAURANTE ("Restaurante", R.drawable.restaurante),
    BAR("Bar", R.drawable.bar),
    …

8.     Añade al principio de la clase MainActivity la siguiente declaración:

public static Lugares lugares = new LugaresVector();

El objeto lugares lugares ha sido declarado con los modificadores public, por lo que será accesible desde cualquier otra clase, y con static, con lo que se va ha crear una instancia única para lugares. Para acceder a este objeto escribiremos  MainActivity.lugares. Este objeto está accesible incluso antes de crear la primera instancia de MainActivity.

9.     Añade en MainActivity el siguiente método:

public void lanzarVistaLugar(View view){
    Intent i = new Intent(this, VistaLugarActivity.class);
    i.putExtra("id", (long)0);
    startActivity(i);
}

Este método lanzará la actividad VistaLugarActivity pasándole como id del lugar a visualizar siempre 0. Más adelante mostraremos algunas alternativas para que el usuario pueda seleccionar el lugar a mostrar.

10.     En el método onOptionsItemSelected() de la actividad MainActivity añade el siguiente código:

@Override public boolean onOptionsItemSelected(MenuItem item) {
       int id = item.getItemId();
       …
       if(id == R.id.menu_buscar) {
             lanzarVistaLugar(null);
             return true;
       }
       …
}

11.     Ejecuta la aplicación. Aparecerá un error cuando selecciones Buscar. Siempre que aparezca un error en ejecución es el momento de visualizar el LogCat. No es sencillo analizar la información que se muestra, pero es muy importante que te acostumbres a buscar la causa del problema en el LogCat. En este caso la información clave se muestra a continuación:

12.     Para resolver el error en ejecución registra la nueva actividad en AndroidManifest.xml.

13.     Ejecuta la aplicación y verifica que cuando seleccionas el icono buscar se arranca una actividad que muestra el primer lugar.

Ejercicio:  Un cuadro de dialogo para indicar el id de lugar

Tras realizar el ejercicio anterior comprobarás que siempre se visualiza el lugar con id = 0    (i.putExtra("id", (long)0)). En este ejercicio vamos a introducir un cuadro de dialogo que permita introducir al usuario el id que desea visualizar. 

 

Ha de quedar claro que esta es la forma más correcta de diseñar el interfaz de usuario. En la siguiente unidad reemplazaremos este cuadro de dialogo por un RecyclerView.

1.      Abre la clase MainActivity del proyecto MisLugares.

2.      Reemplaza el método por el lanzar LanzaVistaLugar() siguiente:

public void lanzarVistaLugar(View view){
    final EditText entrada = new EditText(this);
    entrada.setText("0");
    new AlertDialog.Builder(this)
       .setTitle("Selección de lugar")
       .setMessage("indica su id:")
       .setView(entrada)
       .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int whichButton) {
               long id = Long.parseLong(entrada.getText().toString());
               Intent i = new Intent(MainActivity.this,
                                               VistaLugarActivity.class);
               i.putExtra("id", id);
               startActivity(i);                   
           }})
       .setNegativeButton("Cancelar", null)
       .show();    
}

  Nota sobre Java: En Java es posible crear un objeto sin que este disponga de un identificador de objeto. Este tipo de objeto se conoce como objeto anónimo. El código mostrado a continuación a la derecha es equivalente al de la izquierda.

Clase objeto = new Clase();                new Clase().metodo();
objeto.metodo();

Un objeto anónimo no tiene identificador por lo que solo puede ser usado donde se crea. En el método anterior se ha creado un objeto anónimo de la clase AlertDialog.Builder. Observa cómo no se llama a un método, sino a una cadena de métodos. Esto es posible porque los métodos de la clase AlertDialog.Builder  retornan el objeto  que estamos creando. Por lo tanto, cada método es aplicado al objeto devuelto por el método anterior.

En Android puedes usar la clase AlertDialog para crear un cuadro de dialogo configurable. Si te fijas en la captura anterior están formados por cuatro elementos, de arriba abajo: título, mensaje, vista y botones. Estos elementos pueden ser configurados mediante los método setTitle(), setMessage(), setView(), setPositiveButton() y setNegativeButton().

La vista que se utiliza en este diálogo es un EditText, inicializado con un texto. En caso de necesitar varias entradas se puede crear una vista de tipo layout, que contendría estas entradas. Se han introducido dos botones, indicando el texto del botón y un escuchador de evento que será llamado cuando se pulse el botón. Finalmente se llama al método show() para que se visualice el cuadro de diálogo.

3.      Verifica que funciona correctamente. Pero cuidado, no se verifica que el id sea válido, por lo que ocurrirá un error si es incorrecto.

 Práctica: Ocultar elementos en VistaLugarActivity

En ocasiones no se dispondrá de parte la información de un lugar. En estos casos, puede resultar más conveniente desde un punto de vista estético, no mostrar campos sin información en VistaLugarActivity. Por ejemplo, si el campo de teléfono es igual a 0, podríamos usar el siguiente código para que no se muestre:

if (lugar.getTelefono() == 0) {
   findViewById(R.id.barra_telefono).setVisibility(View.GONE);
} else {
   TextView telefono = (TextView) findViewById(R.id.telefono);
   telefono.setText(Integer.toString(lugar.getTelefono()));
}

Para ocultarlo, en el layout telefono, ponemos el valor propiedad visibility al valor GONE. Este atributo es aplicado a cualquier tipo de vista. Otros posibles valores para este atributo son VISIBLE e INVISIBLE. Tanto con GONE como con INVISIBLE la vista no se verá. Pero con INVISIBLE el espacio ocupado por la vista se mantiene, mientras que con GONE este espacio se elimina.

Trata de realizar un proceso similar a este para los campos dirección, telefono, url y comentario. Para verificar si un  String es vacío puedes usar el método isEmpty().

Ejercicio:  Añadir una barra de acciones a VistaLugar

En este ejercicio vamos a añadir a la actividad un menú a la barra de acciones similar al que se muestra a continuación:

1.     En primer lugar crea el fichero res/menu/vista_lugar.xml que contendrá las acciones a mostrar. Para ello pulsa con el botón derecho sobre la carpeta res/menu y crea el fichero vista_lugar.

2.     Reemplaza su contenido por el siguiente código:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/accion_compartir"
        android:title="compartir"
        android:icon="@android:drawable/ic_menu_share"
        android:orderInCategory="10"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/accion_llegar"
        android:title="cómo llegar"
        android:icon="@android:drawable/ic_menu_directions"
        android:orderInCategory="20"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/accion_editar"
        android:title="editar"
        android:icon="@android:drawable/ic_menu_edit"
        android:orderInCategory="30"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/accion_borrar"
        android:title="borrar"
        android:icon="@android:drawable/ic_menu_delete"
        android:orderInCategory="40"
        app:showAsAction="ifRoom"/>
</menu>

3.     En la clase VistaLugarActivity añade los siguientes métodos:

   @Override
   public boolean onCreateOptionsMenu(Menu menu) {
      getMenuInflater().inflate(R.menu.vista_lugar, menu);
      return true;
   }

   @Override
   public boolean onOptionsItemSelected(MenuItem item) {
      switch (item.getItemId()) {
      case R.id.accion_compartir:
         return true;
      case R.id.accion_llegar:
         return true;     
      case R.id.accion_editar:
         return true;
      case R.id.accion_borrar:
         MainActivity.lugares.borrar((int) id);
         finish();
         return true;
      default:
         return super.onOptionsItemSelected(item);
      }
   }

4.     Ejecuta la aplicación y borra un lugar. Verifica que, si tratas de visualizar el mismo id ahora se muestra el siguiente lugar.

 Práctica: Un cuadro de diálogo para confirmar el borrado

Un usuario podría pulsar por error el botón de borrar, por lo que sería muy conveniente pedir una confirmación antes de borrar.

1.     Crea un nuevo método en la actividad con el siguiente perfil:

public void borrarLugar(final int id) {
   …
}

2.     Modifica el código asociado a case R.id.accion_borrar: para que se llame a este método.

3.     En el método crea un cuadro de dialogo siguiendo el esquema planteado en el ejercicio anterior. Puede ser similar al siguiente.

Creando la actividad EdicionLugarActivity

En este apartado crearemos otra actividad en la aplicación Mis Lugares, EdicionLugarActivity. Esta actividad nos permitirá modificar la mayoría de valores asignados a un lugar (se excluyen los valores que se modifican desde VistaLugarActivity: Valoración, fecha y foto):