Ya, ya, ya, ya.. Si sabemos que nos demoramos, pero es que nos habían escondido el teclado y nos demoramos meses en encontrarlo D:
Hoy continuamos con nuestra saga de Cómo programar Apps para Android desde cero, continuando desde donde dejamos en el número anterior y revisando específicamente 3 temas hoy: El ciclo de vida de una app, como trabajar con vistas y como interactuar con elementos en la pantalla.
El detalle, como siempre, después del salto.
Les recordamos que este tutorial forma parte de la saga Cómo programar apps para Android, el cual ya tiene una parte disponible, que pueden ver en este enlace:
Vamos con lo primero:
Específicamente, lo siguiente:
Sin más que decir, vamos, manos a la obra:
Cuando hablamos de ciclos de vida en una app en Android, específicamente nos referimos a 2: El ciclo de vida de una app propiamente tal, y el ciclo de vida de cada una de las actividades que la componen. ¿En qué se diferencian? Lo veremos ahora:
Ciclo de vida de una app
Como vimos en el número anterior de esta saga, una app esta compuesta por 1 o más actividades (y que en este caso interactúan entre si) y realiza un “camino” desde el momento que la lanzamos hasta que la cerramos por completo.
Este camino se inicia luego de lanzar la app (abrirla desde el dispositivo). Es ahí donde automáticamente se va a la actividad que marcamos inicialmente como launcher (en el Manifest se puede encontrar en la lista de activities, marcada con un intent-filter). De ahí en adelante, el ciclo de vida de la app pasa a mano de las actividades que la componen, cada una de las cuales cuenta con un ciclo de vida propio, que veremos a continuación.
Ciclo de vida de una actividad
Cada actividad que compone una app tiene un ciclo de vida propio, desde el momento en que es llamada (ya sea al inicio o por otra actividad) hasta que es cerrada (parcial o completamente). El ciclo de vida esta compuesto por una serie de estados, que se pueden ver en este diagrama:
El ciclo de vida está representado por una serie de métodos presentes en la clase Activity, los cuales pueden ser extendidos en nuestras propias actividades para agregar funcionalidades según necesitemos. Desde que una actividad se lanza hasta que se cierra o destruye, pasa por varios estados (con sus respectivos métodos), los cuales representan lo siguiente:
En cada uno de estos métodos hay ciertas operaciones que se pueden (y acostumbran hacer) para ayudarnos con tareas específicas que requerirá nuestra aplicación. Iremos viendo más adelante en algunos ejemplos para ir entendiendo mejor este concepto.
Si bien hemos dicho en varias ocasiones que una app es un conjunto de actividades que interactúan entre si, hay un concepto importantísimo que no debemos dejar de lado, que es el de las vistas, ya que como su nombre lo indica, es lo que vemos en la pantalla del dispositivo cada vez que interactuamos con la aplicación.
Concretamente, una vista es un archivo en formato XML que contiene código que representa elementos gráficos que se mostrarán en pantalla. Entre los elementos que se pueden colocar dentro de una vista, están:
Hay una gran cantidad de elementos disponibles para utilizar en las vistas, lo que nos permite crear apps que son tremendamente usables y accesibles para quienes las descarguen y que la interacción sea lo más cómoda posible.
Las vistas o layouts son almacenados en el directorio res/layout de nuestros proyectos:
Si bien el archivo correspondiente a cada vista puede llevar el nombre que sea, es una buena práctica anteponer el activity_ antes del nombre para diferenciarla de otros tipos (que usaremos más adelante) y mantener un mejor orden en el proyecto.
Para agregar elementos a una vista, tenemos 2 opciones: Usar el editor visual que viene en el IDE que usemos (por lo general Eclipse o Android Studio), o bien agregar los elementos directamente desde el código, editando el archivo XML.
El editor visual se ve de esta manera:
Donde cada número es:
Tal como mencionábamos anteriormente, cada elemento que agreguemos a la vista tiene atributos, los cuales representan propiedades sobre como actuará el elemento visualmente. Si bien hay una gran cantidad de atributos, varios de los cuales son exclusivos para ciertos elementos, por lo mínimo siempre trabajaremos con 3:
Ya veremos esto en mayor detalle un poco más adelante.
Si bien todos los elementos gráficos son opcionales para agregar a una vista, hay al menos uno que debe ir si o si cuando la estamos armando, que es un layout.
Layouts
Un layout es un elemento gráfico que ayuda a definir la estructura que tendrá la vista. A esta estructura posteriormente se pueden agregar otros elementos que ingresan anidados y tienen una relacion padre-hijo con el layout (como mencionábamos más arriba).
Existen actualmente distintos tipos de layouts, los cuales se ocupan de acuerdo al tipo de estructura u organización que se necesitará en la vista. Entre ellos, y los más utilizados son estos dos:
Ejemplo de Linear Layout
Ejemplo de Relative Layout
Además de estas dos, hay otras vistas que son utilizadas en diferentes instancias y que iremos viendo a través de ejemplos más adelante para entenderlas mejor. Entre estas podemos encontrar por ej: Gridview (que se utilizan para grillas o cuadrículas), Listview (que se utilizan para listados) y Webview (que se utilizan para representaciones de código Web).
Una vez que definimos el layout base que tendrá la vista, podemos ir agregando otros elementos dentro de el (como los mencionados más arriba), o incluso otros layouts para hacer una sub-organización.
Una actividad y una vista están relacionadas mediante distintos procesos:
Una actividad carga una vista y la muestra en pantalla
Este es el proceso más básico y se puede realizar múltiples veces dentro de una actividad (y en cualquier método propio además de onCreate). Tal como decíamos anteriormente, una actividad puede cargar distintas vistas durante su vida, como de la misma forma una vista puede ser cargada por distintas actividades sin problemas. Esto es muy útil si queremos reutilizar elementos y no hacer copias de la misma vista, que tendrían un alto costo de mantención en caso de hacer cambios.
Para cargar una vista, basta con solo hacer una llamada al método setContentView, pasándole como parámetro el identificador de la vista, de esta manera:
setContentView(R.layout.activity_main)
El parámetro que le pasamos al método es el identificador de la vista, o más bien una referencia hacia ese identificador, que está almacenado en el “famoso” R.java:
¿Qué es el R.java?
R es una clase que se genera automáticamente dentro de las apps que creemos, la cual contiene referencias a todos los recursos del sistema y a las áreas de memoria en donde se encuentran. Ahí encontraremos referencias a recursos como: vistas, elementos gráficos, cadenas de texto, etc.
Cada vez que queramos referenciar algún elemento, ya sea en el editor de vistas o en el código de las actividades, debemos referenciarlo a través de R.
Por ejemplo en el método anterior, como necesitamos un parámetro que referencie una vista, lo que estamos haciendo es obtenerla a través de R, diciéndole que en las vistas que tiene referenciadas, busque la de nombre activity_main (sin la extensión xml).
Eso hará que la vista se muestre en la pantalla del dispositivo.
Una actividad enlaza objetos a elementos de la vista
Este proceso es necesario para poder operar con los elementos visuales de la vista. Por operar nos referimos a poder capturar información que contienen, modificarla y presentar resultados en ellos de ser necesario.
Cada elemento gráfico que agregamos en una vista (botones, checkboxs, textos, imágenes, etc), tiene un objeto equivalente que podemos declarar en la actividad (con su respectiva clase y por ende atributos y métodos), el cual luego enlazamos a cierto elemento del mismo tipo con el método findViewById, pasándole como parámetro el id del elemento y podemos operar sobre él.
Por ejemplo, si en la vista agregamos un elemento del tipo TextView (texto), que tuviese un id texto1, lo enlazamos de esta manera:
TextView texto1 = (TextView) findViewById(R.id.texto1);
Revisando lo anterior podemos ver que declaramos un objeto de tipo TextView llamado texto1 (no es necesario que se llame igual al id del elemento en la vista) y luego le asignamos un valor casteado a tipo TextView y buscamos con el método findViewById el elemento en la vista activa que tiene ese id. Con esa llamada quedará capturado en el objeto y podemos llamar métodos de su clase para operar.
Una actividad captura acciones realizadas sobre los elementos de la vista y realiza operaciones en base a ello
Así como en otros lenguajes, para Android contamos con una funcionalidad muy útil para capturar acciones realizadas sobre los elementos, que son los listeners. Cuando definimos un listener en la actividad, le estamos diciendo a la app a que esté atenta si se realiza alguna acción interactiva con el elemento (un click o un tap por ejemplo) y que cuando eso suceda, ejecute ciertas acciones.
Esto es tremendamente útil para cuando solo queremos ejecutar ciertas funciones después de que pase algo, como por ejemplo si tuvieramos una pantalla de login en nuestra app y queremos que valide solo después de que el usuario presione el botón “Ingresar” por dar un ejemplo.
Estos listeners van asociados a cada elemento (están definidos en sus respectivas clases como métodos) y cada uno de ellos tiene ciertas particularidades que iremos viendo en ejemplos posteriomente.
Una actividad despliega información de resultados en la vista
Tal como capturamos elementos y la información que contienen, muchas clases de objetos tienen métodos que permiten cambiar estos datos (así como sus atributos). Por lo general cada clase cuenta con una serie de métodos que tienen el prefijo set (ej. setText, setColor, setVisibility). Iremos viendo casos a medida que presentemos ejemplos.
Ahora que hemos visto un poco sobre vistas, layouts, elementos y como interactuar con ellos, vamos a aplicarlo en la app que empezamos a trabajar en el número anterior.
La última vez teníamos una actividad llamada MainActivity que tenía el siguiente código:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
Y una vista base llamada activity_main, la cual tiene 2 elementos agregados: Un layout (de tipo RelativeLayout) y un TextView que contiene el texto “Hello world!”, lo cual desde la perspectiva de código se ve así:
Ahora haremos unas leves modificaciones a ambos, partiendo por la vista, a la que le agregaremos un botón (arrastrándolo desde el editor visual a cualquier parte de la pantalla, o agregándolo por código), de manera que el código de la vista se vea así:
Y la vista se vea parecido a esto:
Si queda distinto no se preocupen, lo importante es la funcionalidad que añadiremos. Al botón le cambiamos algunos atributos como el texto (que sale con una advertencia en amarillo porque lo escribimos directamente en vez de asignarle un elemento de tipo cadena, ya veremos esto más adelante) y la posición, pero lo importante es que este presente, da lo mismo el mensaje y donde esté.
Y ahora que hemos agregado el botón, en la actividad haremos algunos cambios en el código para que se vea así (los cambios aparecen en los comentarios del código):
public class MainActivity extends Activity { /* * Declaramos objetos de tipo Button y TextView para capturar * los elementos */ Button boton; TextView texto; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* Cargamos la vista */ setContentView(R.layout.activity_main); /* Capturamos los elementos referenciando sus id's */ boton = (Button) findViewById(R.id.button1); texto = (TextView) findViewById(R.id.textView1); /* Llamamos al método setOnClickListener de la clase Button, * el cual agregará un listener al objeto de manera que cuando sea presionado * (o clickeado), obtendrá el texto actual del objeto tipo TextView y se hará * una comparación: Si dice Hello World lo traducirá a español, de lo contrario * lo escribirá en inglés y se lo asignará al mismo objeto. * * El texto se obtiene con el método getText() y luego se llama de inmediato al * método toString() ya que lo que retorna getText() no es una cadena * y por ende no se puede comparar directamente. * * El método setOnClickListener recibe como parámetro un objeto de tipo * onClickListener y dentro se declara un método onClick que manejará las acciones * a realizar. Esta estructura es siempre la misma para este tipo de listeners. */ boton.setOnClickListener(new OnClickListener() { public void onClick(View v) { if(texto.getText().toString().equalsIgnoreCase("hello world!")) { texto.setText("Hola mundo!"); } else { texto.setText("Hello world!"); } } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
Y ahora si lanzamos la app en el emulador, veremos inicialmente esto:
Y al presionar el botón con el mouse, cambiará a esto:
Y podemos seguir probándolo cuantas veces sea, ya que el listener está preparado para manejar ambos casos sin problemas.
De la misma forma podemos ir probando otros métodos para hacer distintas modificaciones sobre el texto, o incluso sobre el mismo botón. Los instamos a hacer pruebas y experimentar de cara a lo que veremos en el próximo número.
Por ahora lo dejaremos hasta aca, y continuaremos viendo otros contenidos y más ejemplos en la próxima edición, en donde también reforzaremos algunos de los conceptos vistos aca.
Como siempre, si tienen alguna duda o comentario, los invitamos a dejarnos unas líneas a continuación para poder ayudarlos.
Y les recordamos que este tutorial ha sido desarrollado, probado y documentado por el equipo de CLH, por lo que cuenta con nuestro Sello de Garantía:
¡Hasta la próxima!
6:04:16 pm
Hola! Muy bueno está! Pero tarda mucho en publicar la saga :/ a este paso voy a programar en el 2016 algo completo xD
Saludos!
12:25:17 am
Wow, estoy aprendiendo!! porfa apuren la parte III 😀
1:29:09 pm
tenes un simple error en el codigo, que me trabo bastante, cuando creas la clase dentro del metodo setOnClickListener()
debe ser asi: new View.OnClickListener(){… }
falta el View.
12:42:27 am
Jorge,
Si te apareció ese error es que importaste la clase errónea que contenía el método. Cuando Eclipse te marca error en setOnClickListener, debes importar la versión de la clase View y así no es necesario adjuntar el prefijo que indicas. De todas maneras las dos maneras sirven.
Saludos!
3:19:49 pm
[…] Cómo programar apps para Android #2: Ciclo de vida, vistas e interacciones […]