8 ago 2011

Curso de introducción a Android II (Creando nuestra primera aplicación)

En esta segunda entrada del curso de introducción a Android haremos un repaso sobre la estructura que siguen los proyectos Android. Poco a poco iremos definiendo y explicando los conceptos que se utilizan a la hora de crear interfaces de usuario y, cuando terminemos, espero que todos hayamos podido crear y probar nuestra primera aplicación.

Estructura de un proyecto Android:

Lo primero que vamos a hacer es crear un nuevo proyecto Android desde Eclipse (File -> New -> Android Project). Los datos del proyecto serán los siguientes: Project Name: Ejercicio1 Build Target: Android 2.2 Application Name: Ejercicio1 Package Name: com.fluproject.Ejercicio1 Create Activity: Main . Una vez tengamos nuestro nuevo proyecto creado nos encontraremos con la siguiente estructura:

[caption id="attachment_3919" align="aligncenter" width="221" caption="Configuración de proyecto"][/caption]

A continuación pasamos a explicar para qué sirven cada una de las carpetas y ficheros generados:

  • src - En esta carpeta se alojarán todas las clases de nuestra aplicación. Como podemos observar tenemos una única clase Main generada automáticamente al crear el proyecto. Esta será la clase que inicie nuestra futura aplicación. Si abrimos el fichero Main nos encontramos con lo siguiente:
[caption id="attachment_3920" align="aligncenter" width="452" caption="Código inicial"][/caption]

Tenemos una clase Main que hereda de la clase Activity. En nuestro proyecto tendremos tantas clases como vistas (en el futuro las llamaremos actividades) tenga nuestra aplicación y todas deben heredar de Activity. Al heredar de la clase Activity debemos sobrescribir algunos métodos, unos de forma obligatoria y otros de forma optativa si nuestra aplicación lo requiere. Uno de los métodos que siempre hay que sobrescribir es el método onCreate. Este método siempre recibe un objeto de la clase Bundle que hace referencia al estado en el que se encuentra nuestra aplicación, onCreate siempre se activará nada más crearse la aplicación luego siempre tendremos que darle funcionalidad. Tenemos la opción de rescribir más métodos que hace referencia a estados en los que puede entrar nuestra aplicación como por ejemplo onDestroy (método que se activa cuando salimos de la aplicación) u  Como podemos observar dentro del método, se hace una llamada a la clase padre Activity mediante la línea super.onCreate(savedInstanceState), esta sentencia es recomendable hacerla siempre que estemos sobrescribiendo un método de una clase padre. Por último, nos encontramos con la llamada a un método de la clase Activity setContentView que es el encargado de cargar la interfaz que tendrá nuestra actividad, este método recibe una dirección de memoria R.layout.main que apunta a un fichero xml de cuya estructura hablaremos más adelante.

  • res: Vamos a dar un pequeño salto a la carpeta res, sí, ya sé que me he pasado de largo la carpeta gen, pero creo que es lo mejor para seguir una explicación lógica. Esta carpeta va a ser la encargada de guardar todos los recursos que necesitemos usar en nuestra aplicación (imágenes, xml de interfaz, xml de strings, sonidos, audios, etc). Android gestiona los recursos de una forma muy peculiar, a cada recurso le asigna una dirección y lo define como un atributo de una clase llamada R (esta clase se encuentra en la carpeta gen), ¿recordáis como cargábamos la interfaz a nuestra actividad? R.layout.main, más tarde volveremos a ello.
  • Si nos fijamos, tenemos tres carpetas llamadas drawable: drawable-hdpi, drawable-mdpi, drawable-ldpi. En estas carpetas irán alojadas las imágenes de nuestra aplicación y sus extensiones (higth, medium y low) hacen referencia al tipo de densidad de pantalla de nuestro dispositivo o emulador. En principio no es relevante donde coloquemos las imágenes a no ser que queramos optimizar nuestra aplicación. Tenemos la opción de crearnos una carpeta llamada drawable (sin extensión) donde coloquemos todas nuestras imágenes o tenemos la opción de poner las imágenes en cualquiera de las tres, yo personalmente siempre opto por meterlas en drawable-hdpi. Si abrimos cualquiera de las tres carpetas veremos que tenemos una imagen icon.png que corresponde al icono que tendrá nuestra aplicación.
  • La siguiente carpeta es layout. En esta carpeta se encuentran todos los ficheros xml que definen la interfaz de las actividades de nuestra aplicación. Si abrimos la carpeta nos encontramos con un fichero main.xml. Este es el fichero encargado de dar una interfaz a nuestra actividad Main. Si lo abrimos nos encontramos con lo siguiente:
Configurando interfaz

Como podemos observar, disponemos de una paleta llena de componentes para crear interfaces de usuario, podemos añadir botones, listas, etiquetas de texto, imágenes, etc, simplemente arrastrándolos a nuestro "lienzo".  Si pulsamos en la etiqueta main.xml (al lado de Graphical Layout) podemos observar el xml puro y como se definen y programan todos los componentes.

[caption id="attachment_3924" align="aligncenter" width="452" caption="XML de la interfaz"][/caption]

En este xml de interfaz tenemos definido un LinearLayout que no es más que una especie de "contenedor" que albergará componentes de forma consecutiva, en este caso alberga un componente de tipo TextView que es una etiqueta de texto. Cada componente (o tag xml) tiene atributos, en el caso del TextView vemos que tiene un atributo android:text cuyo valor corresponde al contenido textual que tendrá dicha etiqueta de texto. Este valor está almacenado en @string/hello, la variable @string apunta a un fichero xml strings.xml que contiene todos los strings que vayamos a usar en nuestra aplicación.

  • Seguimos con la carpeta values donde nos encontramos con el fichero strings.xml mencionado anteriormente. La filosofía de Android es intentar separar lo máximo posible la parte de la interfaz de la aplicación con respecto a la lógica y a los datos, luego todas las cadenas de texto que utilicemos, ya sean nombres de botones, etiquetas de texto, etc, deberían ir declarados en este fichero.

 

 
Android Resources

De momento, sólo hay declarados dos strings: Un string hello que hace referencia al valor de nuestro TextView, cuyo valor textual es Hello World, Main! y cuyo identificador para poder referenciarlo en los ficheros xml de interfaz (en este caso en el fichero /res/layout/main.xml) es hello. Y, por último, un string app_name cuyo valor es el nombre de nuestra aplicación Ejercicio1 y que más adelante veremos dónde se referencia.

  • Vamos ahora con la carpeta gen, esa que nos hemos saltado anteriormente. Si os acordáis mencioné que Android referenciaba todos los recursos  (imágenes, audios, xml de interfaz, etc, guardados en la carpera res ) en una clase llamada R. Si abrimos la carpeta gen y la clase R podemos comprobarlo nosotros mismos.
[caption id="attachment_3929" align="aligncenter" width="446" caption="Código"][/caption]
  • Si tenemos un poquito de experiencia en Java podremos observar como cada subcarpeta de res está declarada como una clase static y como cada recurso de cada carpeta está declarado como un atributo static de su correspondiente clase. De esta forma, podemos deducir que se puede acceder a cualquier recurso de la aplicación y desde cualquier clase de la forma R.subdirectorio_res.nombre_recurso. Si recordamos, ya lo hicimos anteriormente para cargar la interfaz de la actividad main en la clase /src/Main.java con la sentencia setContentView(R.layout.main).
  • El último fichero que vamos a explicar de la estructura del proyecto es el AndroidManifest.xml. En este fichero se reflejan todos los permisos de nuestra aplicación (permisos para utilizar wifi, bluetooth, gps, etc) y todas las actividades que vayamos creando.  Cuando empecemos con nuestra primera aplicación veremos cómo se declaran determinados permisos.

Nuestra primera aplicación paso a paso:

Esta primera aplicación va a ser muy simple. Tendremos una única actividad Main.java con una interfaz main.xml que contendrá una imagen (banner) y dos botones. Vamos a dar funcionalidad a cada botón, de forma que al pulsar el primero obtengamos la dirección IP de nuestro dispositivo móvil en una red wifi y al pulsar el segundo obtengamos la dirección MAC de la interfaz de red wifi. La aplicación resultante deberá ser exactamente igual a la que os mostramos a continuación:

[caption id="attachment_3932" align="aligncenter" width="467" caption="Primera aplicación Flu Project para Android"][/caption]

Como ya tenemos nuestro proyecto creado vamos a ponernos manos a la obra:

1. Definición de la interfaz de usuario:

Lo primero que vamos a hacer es definir la GUI de nuestra aplicación y a darle una apariencia un poco amigable. Para definir los componentes que tiene la interfaz de nuestra actividad Main debemos dirigirnos al fichero /res/layout/main.xml. Como observamos en las imágenes de la aplicación, debemos incluir un componente imagen ImageView (correspondiente al banner de Flu), dos etiquetas de texto TextView ("Dame mi IP", "Dame mi MAC") y dos botones Button. Para ello abrimos el main.xml, borramos el TextView del helloworld e insertamos los componentes de forma ordenada. Una vez insertados los componentes comprobamos que el xml resultante tenga esta apariencia:

Código

Cargamos ahora los recursos de la carpeta resources que os dejamos adjunta al final del post. Descargamos la carpeta y comprobamos que tenemos tres imágenes (flubanner.png, buttonip.png, buttonmac.png). Arrastramos las tres imágenes a la carpeta res de recursos del proyecto, concretamente a la subcarpeta drawable-hdpi. Una vez cargados los recursos debemos utilizarlos para cada componente, para ello en main.xml localizamos el componente ImageView y en su atributo android:src le indicamos que apunte a la imagen flubanner de esta forma android:src="@drawable/flubanner". En el caso de los Button el proceso cambia un poco, como nuestras botoneras ya contienen texto debemos quitárselo al componente y ponerle a cada uno su correspondiente imagen de fondo. Para ello quitamos a los Button su texto de forma que el atributo text quede así android:text="", después añadimos nosotros mismos otro atributo background que apunte a cada botonera respectivamente android:background="@drawable/buttonip" (En el primer Button) android:background="@drawable/buttonmac" (En el segundo Button). Una vez hecho, podemos ir comprobando que los cambios surgen efecto poniendo nuestro main.xml en modo Graphical Layout.

Creando la interfaz gráfica

Esto tiene muy buena pinta, sólo nos queda poner los TextView en condiciones y acabamos con el diseño de la GUI. ¿Os acordáis de cómo se definen los strings en Android?, pues vamos al fichero /res/values/strings y añadimos dos nuevos. Para ello pulsamos Add.. -> String, al primero le pondremos como nombre string_ip y como valor "Dame mi IP:". Añadimos otro más con nombre string_mac y con valor "Dame mi MAC:". Para finalizar indicamos a los TextView que carguen los strings creados como ya sabemos android:text="@string/string_ip" y android:text="@string/string_mac".

2. Lógica de la aplicación:

Una vez terminada la GUI vamos a pasar a darle funcionalidad a los componentes que hemos definido. Para ello nos vamos a nuestra clase /src/Main.java

Lo primero que vamos a hacer es que nuestros botones respondan a eventos, para ello vamos a cargar dichos botones como atributos privados de la clase y a ponerles a escuchar eventos:

  • Definimos dos atributos privados clase Button (button_ip y button_mac).
  • Cargamos los componentes Button definidos en la interfaz a dichos atributos mediante el método findViewById. Este método carga los componentes mediante el id que le pasemos de cada uno, recordemos que cada componente tiene definido un id como atributo en el xml y este se refleja en la clase R.
  • Ponemos a escuchar eventos a los atributos Button. Para que nuestra actividad escuche eventos nuestra clase Main debe implementar de la clase abstracta OnClickListener. Al hacerlo vemos que el propio Eclipse nos obliga a añadir y a reescribir el método onClick. Una vez añadido el método onClick ponemos los Button (button_ip y button_mac) a escuchar llamando a su atributo setOnClickListener.

De momento, nuestra clase debería tener esta pinta:

[caption id="attachment_3940" align="aligncenter" width="448" caption="Código"][/caption]
  • Debemos indicarle al método onClick qué botón le ha llamado. Como podemos observar el método recibe un objeto de clase View, como apunte, debemos concretar que View es la clase padre de todos los componentes incluido de Button, por lo que nos será fácil filtrar el id del componente pulsado. Para filtrar el Button pulsado sólo tendremos que hacer una comparación del id del Button con un switch o un simple if.
[caption id="attachment_3943" align="aligncenter" width="409" caption="Implementando código "][/caption]
  • Ahora es momento de llamar a la interfaz wifi de nuestro dispositivo para obtener tanto la ip como la mac. Para ello vamos a crearnos dos métodos privados de clase getMyIP y getMyMAC que devuelvan un String que mostraremos en forma de alarma con el contenido correspondiente al pulsar un botón u otro. Dichos métodos tendrán el siguiente aspecto.
[caption id="attachment_3945" align="aligncenter" width="447" caption="Implementando código"][/caption]

Como podemos ver, ambos métodos crean un objeto de clase WifiManager y obtienen la información correspondiente. En el caso de la dirección IP hay que recurrir a un método de la clase Formatter para poder obtenerla en un formato legible.

  • Una vez definidos los métodos, sólo tenemos que llamarlos en el lugar correspondiente y mostrar por pantalla la información obtenida en forma de alerta Toast.
[caption id="attachment_3947" align="aligncenter" width="451" caption="Implementando código"][/caption]
  • Ya tenemos toda la funcionalidad definida de nuestra primera aplicación... peeero, esto ahora mismo no va a funcionar, tenemos que dar permiso a nuestra aplicación para que pueda utilizar la interfaz wifi. Abrimos el fichero AndroidManifest.xml, pulsamos en la pestaña Permissions, Add.. -> Uses Permission y buscamos un permiso de nombre android.permission.ACCESS_WIFI_STATE. Lo dejamos indicado y ya podemos probar nuestra primera aplicación.

Antes de dar por finalizada la entrada hay que resaltar que si probamos la aplicación mediante el emulador obtendremos que nuestra ip es la 0.0.0.0 y nuestra mac es null. Esto es debido a que el acceso a red mediante el emulador se hace a través de un proxi y de un proceso que ya explicaremos en posteriores entradas, por lo tanto la forma idónea de probarla es cargando la aplicación en un dispositivo Android. Para ello no hay más que conectarlo vía USB y ejecutar la aplicación al igual que lo hacemos con el emulador, Eclipse nos reconocerá el teléfono automáticamente y podremos probarla sin problemas. No dudéis en escribir para cualquier tipo de duda, dentro de dos semanas más!

Descarga Proyecto

Recursos Proyecto

 

===============================================================

Curso de introducción a Android (Instalación del Android SDK + Hello World)

Curso de introducción a Android II (Creando nuestra primera aplicación)

Curso de introducción a Android III (Escáner de redes WIFI)

Curso de introducción a Android IV (Crackeando redes Wifi)

===============================================================

 

 

20 comentarios:

  1. [...] This post was Twitted by jantonioCalles [...]

    ResponderEliminar
  2. [...] - Curso de introducción a Android II (Creando nuestra primera aplicación) [...]

    ResponderEliminar
  3. Genial, continua así!!! Tu web es de mucha utilidad para los que nos gustaria aprender Android!

    ResponderEliminar
  4. Muy buen tutorial, se entiende todo a la perfección. Sigue así.

    ResponderEliminar
  5. ¿De dondeme bajo la carpeta con los las imagenes para los botones?Esperemos que este curso siga adelante.

    ResponderEliminar
  6. @jorge ya lo tienes disponible ;) en el paquete de recursos en el mismo post ;)

    ResponderEliminar
  7. ¿Como se podria quitar del programa la barrita azul donde pone ejercicio1?

    ResponderEliminar
  8. Ya he encontrado la forma de quitarlo con estas sentencias:requestWindowFeature(Window.FEATURE_NO_TITLE);getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);

    ResponderEliminar
  9. Absolutamente genial el tutorial. Muchísimas gracias!

    ResponderEliminar
  10. No me rula el ejemplo, al pulsar cualquiera de los dos botones se cierra "inesperadamente" tanto en el avd como en el móvil (wildfire con android 2.2.1).He repasado todo y parece que no hay diferencias.He probado a ejecutarlo con Debug (F11) pero no me aclaro, aunque parece que el problema está en WifiManager, o algo eso me ha parecido entender de «IWifiManager$Stub$Proxy.getConnectionInfo() line: 567» en cuya vista me dice «Source not found». Parece que lo tengo todo instalado y actualizado. ¿Alguna sugerencia al respecto?De todas maneras aún me ha servido para aprender lo básico.

    ResponderEliminar
  11. Arreglado.Funcionó con los archivos que proporcionais, así que hice una comparación archivo por archivo con Diff Meld y después de reordenar el código ha funcionado.Aún no entiendo muy bien cual era el problema, pero ahora sí funciona.

    ResponderEliminar
  12. Muy interesante el curso. He leído los 8 posts que llevas publicados sobre android, pero todavía no has desvelado cómo se resuelve el tema de la red en el emulador, dices al final de este post que es con un proxy y un proceso. ¿sabes de alguna página donde expliquen cómo hacerlo?Gracias

    ResponderEliminar
  13. Buenas Randy!Realmente el tráfico de red que generas mediante el emulador se enruta a través de tu equipo de desarrollo... luego tienes disponible salida a internet mediante el emulador. La forma a la que puedes dirigirte a recursos de red alojados en tu equipo desde el emulador es mediante la ip que tiene el emulador reservada para el equipo en el que estás desarrollando que es la 10.0.2.2Conclusión, para referirte a localhost desde el emulador debes referirte a la ip 10.0.2.2Espero que te haya aclarado algo mi respuesta, si no es así no dudes en volver a preguntar :)Un saludo!

    ResponderEliminar
  14. Esta muy bien el manual pero no indicas que clases se deben cargar con la sentencia import. Por ejemplo no me reconocía la clase Button hasta que hice un import android.widget.*; ak principio del Main.java

    ResponderEliminar
  15. Excelente tutorial, despues de tanto buscar tutoriales al fin encuentro uno practico y comprensible =) muchisimas gracias y espero que el toturial continue, podrias montar un ejercicio con acceso a base de datos?

    ResponderEliminar
  16. Muchas Gracias! Excelente Tutorial y los demás también, han sido de mucha ayuda, tus temas son bastantes directos, poco comunes y con ejemplos prácticos y de basta utilidad, explicas de maravilla. Sigue Asi!

    ResponderEliminar
  17. Magnifico el ejercicio! muy claro y legible todos los pasos. Y también muy bien sintetizado cada explicación, para no aburrir al personal.Gracias!

    ResponderEliminar
  18. Muy bueno el tutorial :)Hay una duda que tengo.Cuando definimos las imagenes de los botones, como sabes que se ha de poner en:android:id=""y para que sirve?jaja disculpa las molestias.Gracias!

    ResponderEliminar