Primeros Pasos en Android: 1.1. Cómo Las Aplicaciones Proporcionan Puntos de Entrada
Primeros Pasos en Android: 1.1. Cómo Las Aplicaciones Proporcionan Puntos de Entrada
Primeros Pasos en Android: 1.1. Cómo Las Aplicaciones Proporcionan Puntos de Entrada
Antes de comenzar, analizaremos dos conceptos fundamentales que debemos comprender sobre las aplicaciones
en Android: cómo las aplicaciones proporcionan puntos de entrada y cómo estas se adaptan a los diferentes
dispositivos.
Las aplicaciones para Android se compilan como una combinación de componentes que pueden invocarse de
manera individual. Por ejemplo, una actividad es un tipo de componente de aplicación que proporciona una
interfaz de usuario (IU).
La actividad "principal" (Main Activity) comienza cuando el usuario presiona el ícono de la aplicación.
También podemos dirigir al usuario a una actividad de otro lugar, como una notificación o incluso una
aplicación diferente.
Otros componentes, como los receptores de emisión y los servicios, permiten que nuestra aplicación realice
tareas en segundo plano sin una IU.
Android nos permite proporcionar diferentes recursos para distintos dispositivos. Por ejemplo, podemos crear
diferentes diseños para distintos tamaños de pantalla. El sistema determina qué diseño usar en función del
tamaño de la pantalla del dispositivo actual.
Si alguna de las funciones de nuestra aplicación necesita hardware específico, como una cámara, durante el
tiempo de ejecución, podemos consultar si el dispositivo tiene ese hardware o no, y luego inhabilitar las
funciones correspondientes si no lo tiene. Podemos especificar que nuestra aplicación requiera ciertos tipos de
hardware a fin de que Google Play no permita que se instale la aplicación en dispositivos que no los tengan.
Ahora veremos cómo crear una aplicación simple en Android. Nuestra aplicación contará de una única pantalla
(Activity) en la cual mostraremos el mensaje “Hola Mundo”. Lo primero que debemos saber es que la estructura
de un proyecto de una Aplicación Android está dividida en secciones. Una sección sirve para alojar todo el
código JAVA, mientras que otra sección sirve para alojar recursos como imágenes, sonidos, definiciones de
interfaces, etc. Esta sección de recursos a su vez también se divide en diferentes secciones según lo
necesitemos, como layouts, menus, drawable, etc. También hay una sección que permite definir las cualidades
de nuestra aplicación en un archivo de manifiesto (AndroidManifest). La estructura típica de un proyecto se
puede apreciar en la figura 1.1.
Figura 1.1: Estructura de un proyecto Android
En la imagen se puede apreciar la carpeta manifests, java, y res. En la carpeta manifests solamente se
encuentran el archivo AndroidManifest.xml. En la carpeta Java encontramos los archivos de código Java. Y en
la carpeta res (resources – Recursos) están las diferentes categorías de recursos que nuestra aplicación puede
necesitar. Algunas de las que mas vamos a necesitar son la carpeta de layout y la carpeta de values.
Para crear nuestra aplicación simple hemos creado un proyecto que contiene una archivo MainActivity.java en
la carpeta java en el paquete hn.edu.unah.holamundo. También se ha creado un archivo activity_main.xml en la
carpeta de layout de la carpeta res tal como se muestra en la figura 1.2.
NOTA: En la imagen se puede apreciar que en la carpeta de java el archivo MainActivity no se muestra la
extensión (es decir que no se muestra como MainActivity.java sino como MainActivity). Esto es así porque
aquí solo se muestra el nombre de la clase no el nombre del archivo recordemos que en java una clase publica
debe estar guardada en un archivo con el mismo nombre y la extensión java.
Ahora vamos a analizar el código de nuestros archivos activity_main.xml y MainActivity.java En primer lugar
vamos a revisar el código de nuestra interfaz, es decir el archivo activity_main.xml.
Dentro de nuestro LinearLayout hemos incluido un TextView al cual le hemos establecido el atributo text con el
valor de “Hola Mundo”. También hemos establecido los atributos layout_width y layout_height con el valor de
“wrap_content”.
Ahora vamos a revisar el código de nuestro archivo MainActivity.java. Realmente para este ejemplo nosotros
no hemos modificado nada ya que este es el código que genera Android estudio al momento de crear nuestra
aplicación.
package hn.edu.unah.holamundo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Los mas resaltable de este código es que nuestra clase debe extender de la clase Activity o de una clase que
extienda de Activity, en este caso extiende de AppCompatActivity. Dentro de esta clase se ha sobrescrito
(@Override) el método onCreate en el cual solamente se incluyen dos líneas de código. La primera línea solo
hace una invocación a la versión onCreate de la clase base. La segunda línea es la que indica de donde se toma
la interfaz de nuestra actividad. En este caso es del archivo activity_main. El resultado de ejecutar esta apliacion
es el que se muestra en la figura 1.3.
La aplicación que hemos creado permite apreciar algunas características del desarrollo de aplicaciones Android.
Ahora vamos a darle un poco mas de vida permitiendo que el usuario interactúe con ella. Vamos a agregar un
botón que el usuario podrá presionar y nuestra aplicación reaccionará a esa acción. Al momento que el usuario
presione el botón la aplicación mostrará un mensaje dando la bienvenida.
Lo primero que haremos será trabajar nuestra interfaz de usuario, en la cual agregaremos un botón. Los botones
en Android, al igual que los TextView, son vistas (Views). Las vistas son los componentes que utilizaremos
para crear nuestras interfaces de usuario UI. El LinearLayout también es una vista al igual que las anteriores
mencionadas. Abordaremos esto más adelante. El código de nuestro activity_main.xml es el que se muestra a
continuación:
1.5. Conclusión
Como pudimos ver en este capítulo, desarrollar aplicaciones en Android son bastante sencillo ya que los
proyectos en Android Studio aplicaciones separan apropiadamente los recurso (imágenes, interfaces de usuario,
sonidos) del código java lo que permite manipular más fácilmente cada uno de estos elementos enfocándonos
solamente en lo que compete a cada elemento sin mezclar, por ejemplo, el código que define la funcionalidad de
la aplicación con la interfaz de esta.
2. Recurso y AndroidManifest
En este capitulo trataremos algunos aspectos conceptuales que necesitamos saber para poder
desarrollar aplicaciones en Android. Estos son los recursos y el archivo de manifiesto de Android
(AndroidManifest)
Los recursos son los archivos adicionales y el contenido estático que usa nuestro código, como
mapas de bits, definiciones de diseño, strings de interfaz de usuario, instrucciones de animación, etc.
Siempre debemos externalizar los recursos para aplicaciones, como imágenes y strings del resto de
nuestro código, para que podamos mantenerlos de forma independiente. También debemos
proporcionar recursos alternativos para configuraciones de dispositivos específicos, agrupándolos en
directorios de recursos con un nombre especial. Durante la ejecución, Android utiliza el recurso
adecuado según la configuración actual. Por ejemplo, podemos proporcionar un diseño de interfaz
de usuario (IU) diferente según el tamaño de la pantalla o strings diferentes según la configuración
de idioma.
Una vez que externalizas los recursos para nuestra aplicación, podemos acceder a ellos a través los
ID de recursos que se generan en la clase R de nuestro proyecto. En este capítulo veremos cómo
podemos agrupar los recursos en un proyecto de Android y acceder a ellos desde el código de
nuestra aplicación u otros archivos XML posteriormente.
Los recursos de nuestra aplicación pueden ser de diferentes tipos y es necesario ubicarlos
apropiadamente dentro de la carpeta correspondiente. Todas las carpetas de recursos los
colocaremos dentro de la carpeta res. La tabla 3.1 muestra los diferentes tipos de carpetas que
podemos tener dentro de nuestra carpeta de recursos.
Todos los proyectos de aplicaciones deben tener un archivo AndroidManifest.xml (con ese mismo
nombre) en la raíz de la fuente del proyecto. El archivo de manifiesto describe información esencial
de nuestra aplicación para las herramientas de creación de Android, el sistema operativo Android y
Google Play.
Entre muchas otras cosas, el archivo de manifiesto debe declarar lo siguiente:
El nombre del paquete de la aplicación, que normalmente coincide con el espacio de nombres de tu
código. Las herramientas de compilación de Android usan esto para determinar la ubicación de las
entidades de código cuando se compila el proyecto. Al empaquetar la aplicación, las herramientas de
compilación sustituyen este valor por el ID de aplicación de los archivos de compilación de Gradle, que
se utiliza como identificador único de la aplicación en el sistema y en Google Play. Obtén más
información sobre el nombre del paquete y el ID de la aplicación.
Los componentes de la aplicación, que incluyen todas las actividades, servicios, receptores de
emisiones y proveedores de contenido. Cada componente debe definir propiedades básicas, como el
nombre de su clase Kotlin o Java. También puede declarar capacidades, como las configuraciones de
dispositivos que puede manejar, además de filtros de intents que describen cómo se puede iniciar el
componente. Obtén más información sobre los componentes de la aplicación.
Los permisos que necesita la aplicación para acceder a las partes protegidas del sistema o a otras
aplicaciones. También declara cualquier permiso que otras aplicaciones deben tener si quieren
acceder al contenido de esta aplicación. Obtén más información sobre los permisos.
Las funciones de hardware y software que requiere la aplicación afectan a los dispositivos que pueden
instalar la aplicación desde Google Play. Obtén más información sobre la compatibilidad de
dispositivos.
Si usamos Android Studio para crear una aplicación, se generará el archivo de manifiesto
automáticamente, y la mayoría de los elementos esenciales de este se irán agregando a medida que
compilemos la aplicación (especialmente cuando usemos plantillas de código).
El elemento raíz del archivo de manifiesto requiere un atributo para el nombre del paquete de tu
aplicación (que normalmente coincide con la estructura del directorio del proyecto: el espacio de
nombres de Java).
Aplica este nombre como espacio de nombres para la clase R.java generada de tu app (se usa para
acceder a los recursos de la aplicación). Ejemplo: Con el manifiesto anterior, se crea la clase R en
com.example.myapp.R.
Usa este nombre para resolver cualquier nombre de clase relativo que se declare en el archivo de
manifiesto. Ejemplo: Con el manifiesto anterior, se resuelve una actividad declarada como <activity
android:name=".MainActivity"> y pasa a ser com.example.myapp.MainActivity.
Como tal, el nombre del atributo package del manifiesto siempre debe coincidir con el nombre de
paquete básico de tu proyecto, en el cual conservas tus actividades y otros tipos de código de la
aplicación. Por supuesto, puedes tener otros subpaquetes en tu proyecto, pero esos archivos deben
importar la clase R.java usando el espacio de nombres del atributo package.
Componentes de la aplicación
Por cada componente de la aplicación que creemos en nuestro proyecto, deberemos declarar un
elemento XML correspondiente en el archivo de manifiesto:
Permisos
Las aplicaciones de Android deben solicitar permiso para acceder a datos del usuario confidenciales
(como contactos y SMS) o a determinadas funciones del sistema (como la cámara y el acceso a
Internet).
A partir de Android 6.0 (nivel de API 23), el usuario puede aprobar o rechazar algunos permisos
durante el tiempo de ejecución. No obstante, sin importar qué versión de Android admita tu
aplicación, debes declarar todas las solicitudes de permisos con un elemento <uses-permission> en
el manifiesto. Si se otorga el permiso, la aplicación puede usar las funciones protegidas. De lo
contrario, los intentos de acceder a esas funciones fallarán.
El XML que aparece a continuación es un AndroidManifest.xml simple de ejemplo que declara dos
actividades de la aplicación.
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0"
package="com.example.myapp">
<!-- Beware that these values are overridden by the build.gradle file -->
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="26" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".DisplayMessageActivity"
android:parentActivityName=".MainActivity" />
</application>
</manifest>
3.4. Permisos de Apps
El objetivo de un permiso es proteger la privacidad del usuario de Android. Las aplicaciones para
Android deben solicitar permiso para acceder a datos sensibles del usuario (como contactos y SMS)
y algunas funciones del sistema (como Internet y la cámara). Según la función, el sistema podría
otorgar automáticamente el permiso o pedirle al usuario que apruebe la solicitud.
Un punto central del diseño de la arquitectura de seguridad de Android consiste en que ninguna
aplicación, de forma predeterminada, tiene permiso para realizar operaciones que pudieran tener
consecuencias negativas para otras aplicaciones, el sistema operativo o el usuario. Esto incluye leer
datos privados de los usuarios o escribir en ellos (por ejemplo, los contactos o los mensajes de
correo electrónico), leer archivos de otra aplicación o escribir en ellos, acceder a una red, mantener
el dispositivo activo, etcétera.
Aprobación de permisos
Una aplicación debe publicitar los permisos que requiere incluyendo etiquetas <uses-permission> en
el manifiesto de la app. Por ejemplo, en el manifiesto de una app que necesite enviar mensajes SMS,
debería incluirse esta línea:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.snazzyapp">
Si en el manifiesto de nuestra aplicación se incluyen permisos riesgosos (es decir, los que podrían
afectar la privacidad del usuario o el funcionamiento normal del dispositivo), como el permiso de
SEND_SMS del ejemplo anterior, el sistema solicitará al usuario que otorgue explícitamente esos
permisos.
Solo los permisos riesgosos requieren la aprobación del usuario. El modo en el que Android le pide
al usuario que otorgue permisos riesgosos depende de la versión del sistema que se ejecuta en el
dispositivo del usuario y de la versión del sistema a la que se orienta nuestra aplicación.
Si el usuario rechaza la solicitud de permiso, la próxima vez que la aplicación solicite el permiso, el
diálogo incluirá una casilla de verificación que, si se marca, indicará que el usuario no desea que se
le vuelva a pedir el permiso.
Incluso si el usuario le otorga a la aplicación el permiso que solicitó, esto no te garantizará que
siempre lo tendrá. Además, los usuarios tienen la opción de habilitar o inhabilitar permisos uno por
uno en la configuración del sistema. Siempre deberíamos verificar y solicitar permisos durante el
tiempo de ejecución, a fin de evitar errores durante ese momento.
Si el usuario hace clic en Aceptar, se otorgarán todos los permisos solicitados por la aplicación. Si el
usuario rechaza la solicitud de permisos, el sistema cancelará la instalación de la aplicación.
3.5. Conclusión
La separación del código y los recursos en una aplicación nos permite a nosotros como
desarrolladores tener un mayor control de los diferentes componentes que integran un proyecto de
aplicación así como mecanismo para dar soporte a diferentes tipos de dispositivos.
2.1. Entendiendo el ciclo de vida de una Activity
Cuando un usuario navega por nuestra aplicación, las instancias de Activity atraviesan diferentes
estados de su ciclo de vida. La clase Activity, y todas las que extienden de ella, proporciona una
serie de métodos callback que permiten a la actividad saber que un estado cambió, esto es, saber
cuándo el sistema está creando, deteniendo o reanudando una actividad, o si el proceso en que se
encuentra a finalizado.
Con los callbacks de un ciclo de vida, podemos definir como se comportará una actividad cuando, el
usuario inicie, abandone o reanude esta. Por ejemplo, si creamos una aplicación para reproductor de
video, podemos pausar el video cuando el usuario cambie a otra aplicación. Cuando el usuario
regrese, podríamos reanudar la reproducción el video desde el mismo punto en que se pausó. Los
callbacks permiten realizar un trabajo específico que sea apropiado para cada cambio de estado de
aplicación esto nos permite hacer aplicaciones más sólidas y eficientes. Una buena implementación
de los callbacks del ciclo de vida nos puede ayudar a garantizar que nuestra aplicación:
No falle si el usuario recibe una llamada telefónica o cambia a otra aplicación mientras usa la nuestra.
No consuma recursos valiosos del sistema cuando el usuario no la use de forma activa.
No pierda el progreso del usuario si este abandona nuestra aplicación y regresa a ella posteriormente.
No falle ni pierda el progreso del usuario cuando se gire la pantalla entre la orientación horizontal y la
vertical.
Como mencionamos anteriormente, para manejar las transiciones entre las etapas del ciclo de vida
de una actividad, la clase Activity proporciona un conjunto básico de seis métodos callback:
onCreate(), onStart(), onResume(), onPause(), onStop() y onDestroy(). Android se encarga de
invocar cada uno de estos métodos activity entra en un nuevo estado. En la figura 2.1, se muestra
una representación visual de cómo trabajan estos métodos durante el ciclo de vida de una actividad.
onCreate()
Este método siempre los debemos implementar ya que se activa cuando el sistema crea la actividad
por primera vez. Cuando se crea la actividad, esta entra en el estado Created. En el método
onCreate(), ejecutaremos la lógica de arranque básica de la aplicación que debe ocurrir una sola vez
en todo el ciclo de vida de la actividad. Este método recibe el parámetro savedInstanceState, que es
un objeto Bundle que contiene el estado ya guardado de la activity. Si la actividad nunca ha existido,
el valor del objeto Bundle es nulo.
onStart()
Cuando la actividad entra en el estado Started, el sistema invoca este método. onStart() hace que el
usuario pueda ver la actividad, mientras la aplicación se prepara para que esta entre en primer plano
y se convierta en interactiva. Por ejemplo, este método es donde la aplicación inicializa el código que
mantiene la interfaz de usuario (IU). El método onStart() se completa muy rápido y, al igual que con
el estado Created, la actividad no permanece en el estado Started. Una vez finalizada este método,
la actividad entra en el estado Resumed, y el sistema invoca el método onResume().
onResume()
Cuando la actividad entra en el estado Resumed, pasa al primer plano y, a continuación, el sistema
invoca la devolución de llamada onResume(). Este es el estado en el que la app interactúa con el
usuario. La app permanece en este estado hasta que ocurre algún evento que la quita de foco. Tal
evento podría ser, por ejemplo, recibir una llamada telefónica, que el usuario navegue a otra
actividad o que se apague la pantalla del dispositivo. Cuando se produce un evento interrumpa la
actividad, esta entra en el estado Paused y el sistema invoca la devolución de llamada onPause(). Si
la actividad regresa al estado Resumed desde Paused, el sistema volverá a llamar al método
onResume(). Por esta razón, debemos implementar onResume() para inicializar los componentes
que lancemos en onPause() y realizar otras inicializaciones que deban ejecutarse cada vez que la
actividad entre en el estado Resumed.
onPause()
Android llama a este método a modo de primera indicación de que:
el usuario está abandonando nuestra actividad (aunque no siempre significa que está finalizando la
actividad);
indica que la actividad ya no está en primer plano (aunque puede seguir siendo visible si el usuario
está en el modo multiventana).
Debemos utilizar el método onPause() para pausar o ajustar las operaciones que no deben continuar
(o que deben continuar con moderación) mientras la activity se encuentra en estado Paused, y que
esperamos reanudar en breve. Hay varias razones por las que una actividad puede entrar en este
estado. Por ejemplo:
Algunos eventos interrumpen la ejecución de la app, como se describe en la sección onResume(). Este
es el caso más común.
En Android 7.0 (API nivel 24) o versiones posteriores, varias apps se ejecutan en el modo
multiventana. Debido a que solo una de las apps (ventanas) tiene foco en cualquier momento, el
sistema pausa todas las demás.
Se abre una nueva actividad semitransparente (como un diálogo). Mientras la actividad siga siendo
parcialmente visible, pero no esté en foco, se mantendrá pausada.
También puedemos utilizar el método onPause() para liberar recursos del sistema, controladores de
sensores (como el GPS) o cualquier otro recurso que pueda afectar la duración de la batería
mientras nuestra actividad esté en pausa y el usuario no los necesite.
onStop()
Cuando el usuario ya no puede ver tu actividad, significa que ha entrado en el estado Stopped, y el
sistema invoca la devolución de llamada onStop(). Esto puede ocurrir, por ejemplo, cuando una
actividad recién lanzada cubre toda la pantalla. El sistema también puede llamar a onStop() cuando
haya terminado la actividad y esté a punto de finalizar.
En el método onStop(), la app debe liberar o ajustar los recursos que no son necesarios mientras no
sea visible para el usuario. Por ejemplo, nuestra aplicación podría pausar animaciones o cambiar de
actualizaciones de ubicación detalladas a más generales. Usar onStop() en lugar de onPause()
garantiza que continúe el trabajo relacionado con la IU, incluso cuando el usuario esté viendo
nuestra activity en el modo multiventana.
Desde el estado Stopped, la actividad regresa a interactuar con el usuario o se termina de ejecutar y
desaparece. Si regresa la actividad, el sistema llamará a onRestart(). Si se terminó de ejecutar
Activity, el sistema llamará a onDestroy(). En la siguiente sección, se explica la devolución de
llamada onDestroy().
onDestroy()
Se llama a onDestroy() antes de que finalice la actividad. El sistema invoca este método por los
siguientes motivos:
La actividad está terminando (debido a que el usuario la descarta por completo o a que se llama a
finish()).
El sistema está finalizando temporalmente la actividad debido a un cambio de configuración (como la
rotación de la pantalla o el modo multiventana).
Podemos diferenciar estos dos casos con el método utilizando el método isFinishing().
Si la actividad está terminando, onDestroy() es el método del ciclo de vida final que recibe la
actividad. Si se llama a onDestroy() como resultado de un cambio de configuración, el sistema crea
inmediatamente una nueva instancia de Activity y luego llama a onCreate() en esa nueva instancia
en la nueva configuración.
El método onDestroy() debe liberar todos los recursos que aún no han sido liberados por
devoluciones de llamada anteriores, como onStop().
El sistema finaliza los procesos cuando necesita liberar RAM; la probabilidad de que el sistema
finalice un proceso determinado dependerá del estado del proceso en ese momento. El estado del
proceso, a su vez, depende del estado de la actividad que se ejecuta en el proceso. La tabla 2.1
muestra la correlación entre el estado del proceso, el estado de la actividad y la probabilidad de que
el sistema finalice el proceso.
Resumed
Más Segundo plano (foco perdido) Paused
Mayor Segundo plano (no visible) Stopped
Vacío Destroyed
Tabla 2.1: Relación entre el ciclo de vida del proceso y el estado de la actividad
El sistema nunca finaliza una actividad de forma directa para liberar memoria. En su lugar, finaliza el
proceso en el que se ejecuta la actividad para eliminar no solo la actividad, sino también todo lo que
se ejecuta en el proceso. Para aprender a preservar y restaurar el estado de la IU de tu actividad
cuando finaliza el proceso iniciado por el sistema, consulta Cómo guardar y restablecer el estado de
una actividad. Un usuario también puede finalizar un proceso utilizando el Administrador de
aplicaciones de Configuración para finalizar la aplicación correspondiente.
Ahora vamos a desarrollar una pequeña aplicación que demuestre el funcionamiento los métodos
involucrados en el ciclo de vida de una actividad. Para ver el comportamiento de cada método hemos
incluido un mensaje que se mostrará cada vez que uno de los métodos se ejecute. El mensaje
indicará que método es el que se está llevando a cabo.
MainActivty.java
package hn.edu.unah.ciclovida;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(this, "Actividad Creada", Toast.LENGTH_SHORT).show();
}
@Override
protected void onStart() {
super.onStart();
Toast.makeText(this, "Actividad Iniciada", Toast.LENGTH_SHORT).show();
}
@Override
protected void onResume() {
super.onResume();
Toast.makeText(this, "Actividad Reanudada", Toast.LENGTH_SHORT).show();
}
@Override
protected void onPause() {
Toast.makeText(this, "Actividad Pausada", Toast.LENGTH_SHORT).show();
super.onPause();
}
@Override
protected void onStop() {
Toast.makeText(this, "Actividad Detenida", Toast.LENGTH_SHORT).show();
super.onStop();
}
@Override
protected void onDestroy() {
Toast.makeText(this, "Actividad Destruida", Toast.LENGTH_SHORT).show();
super.onDestroy();
}
}
Se puede apreciar que en los métodos onCreate, onStart, onResume, siempre se invoca primero el
método de la super clase y luego ejecutamos nuestro código. En contraposición, en los métodos
onPause, onStop, onDestroy siempre ejecutaremos nuestro código y luego invocaremos el método
de la super clase.
activity_main.xml
2.5. Conclusión
Los métodos callback del ciclo de vida nos proporcionan un mecanismo para controlar los diferentes
cambios de estado de una Activity lo que nos permite crear aplicaciones mas confiables y robustas
que se comporte como el usuario espera.
4. Layouts
Un diseño (Layout) define la estructura de la interfaz de usuario en nuestra aplicación, como, por
ejemplo, en una actividad. Todos los elementos del diseño se crean usando una jerarquía de objetos
View y ViewGroup. Una View suele mostrar un elemento que el usuario puede ver y con el que
puede interactuar. Por su parte, ViewGroup es un contenedor invisible que define la estructura de
diseño de View y otros objetos ViewGroup.
Los objetos View suelen llamarse "widgets" y pueden ser una de las muchas subclases, como Button
o TextView. Los objetos ViewGroup se denominan generalmente "diseños" y pueden ser de muchos
tipos que proporcionan una estructura diferente, como LinearLayout o RelativeLayout.
Declarar elementos de la IU en XML. Android proporciona un vocabulario XML simple que coincide con
las clases y subclases de vistas, como las que se usan para widgets y diseños.
También puedes utilizar la función Layout Editor de Android Studio para crear tu diseño XML mediante
una interfaz de arrastrar y soltar.
Crear una instancia de elementos de diseño durante el tiempo de ejecución. Nuestra aplicación
puede crear objetos View y ViewGroup (y manipular sus propiedades) de forma programática.
Declarar la IU en XML nos permite separar la presentación de nuestra aplicación del código que
controla su comportamiento. El uso de archivos XML también facilita la creación de diferentes
diseños para diferentes tamaños de pantalla y orientaciones. Este es el método que vamos a utilizar
a lo largo de este curso.
4.1. LinearLayout
Diseño lineal o LinearLayout es un grupo de vista que alinea todos los campos secundarios en una
única dirección, de manera vertical u horizontal. Podemos especificar la dirección del diseño con el
atributo android:orientation.
Todos los campos secundarios de un LinearLayout se apilan uno detrás de otro, por lo cual una lista
vertical solo tendrá un campo secundario por fila, independientemente del ancho que tengan, y una
lista horizontal solo tendrá la altura de una fila (la altura del campo secundario más alto, más el
relleno). Un LinearLayout respeta los márgenes entre los campos secundarios y la gravedad
(alineación a la derecha, centrada o a la izquierda) de cada campo secundario.
Distribución equitativa
Para crear un diseño lineal en el que cada campo secundario use la misma cantidad de espacio en la
pantalla, define el android:layout_height de cada vista en "0dp" (para un diseño vertical) o el
android:layout_width de cada vista en "0dp" (para un diseño horizontal). Luego, fija el
android:layout_weight de cada vista en "1".
Distribución no equitativa
También podemos crear diseños lineales en los que los elementos secundarios utilicen diferentes
cantidades de espacio en la pantalla:
Si hay tres campos de texto y dos de ellos declaran un volumen igual a 1 mientras al restante no se le
asigna volumen, el tercer campo de texto sin volumen no se expandirá. En cambio, solo ocupará el
área que requiera su contenido. Los otros dos campos de texto, por otro lado, se expandirán de
manera equitativa a fin de llenar el espacio restante después de que se midan los tres campos.
Si hay tres campos de texto y dos de ellos declaran un volumen de 1 mientras al tercer campo se le
asigna un volumen de 2 (en lugar de 0), entonces ahora se declara más importante que los otros dos,
por lo que obtiene la mitad del espacio total restante, mientras que los dos primeros comparten el resto
por igual.
El siguiente de código muestra cómo podemos utilizar combinaciones de LinearLayout para crear
una interfaz como la que se muestra en la figura 4.1
activity_main.xml
4.2. RelativeLayout
RelativeLayout es un elemento muy eficaz para diseñar una interfaz de usuario porque puede
eliminar grupos de vistas anidados y conservar la estabilidad de la jerarquía de diseño, lo que mejora
el rendimiento. Si descubrimos que usamos varios grupos de LinearLayout anidados, podemos
reemplazarlos por un solo RelativeLayout.
RelativeLayout permite que las vistas secundarias especifiquen su posición relativa a la vista
superior o entre sí (especificada por ID). De esta manera, podemos alinear dos elementos por el
borde derecho o hacer que uno esté por debajo del otro, en el centro de la pantalla, en el centro a la
izquierda, y así sucesivamente. De manera predeterminada, todas las vistas secundarias se dibujan
en la esquina superior izquierda del diseño, por lo que debemoss definir la posición de cada una de
las vistas utilizando las diversas propiedades de diseño disponibles del RelativeLayout.
Entre algunas de las muchas propiedades de diseño disponibles para las vistas de un grupo
RelativeLayout, se incluyen las siguientes:
android:layout_alignParentTop: Si el valor es "true", el borde superior de esta vista coincidirá con el del
elemento superior.
android:layout_alignParentBottom: Si el valor es "true", el borde inferior de esta vista coincidirá con el
del elemento inferior.
android:layout_alignParentLeft: Si el valor es "true", el borde izquierdo de esta vista coincidirá con el
del elemento izquierdo.
android:layout_alignParentRight: Si el valor es "true", el borde derecho de esta vista coincidirá con el
del elemento derecho.
android:layout_below: Posiciona el borde superior de esta vista debajo de la vista especificada con un
ID de recurso.
android:layout_above: Posiciona el borde inferior de esta vista encima de la vista especificada con un
ID de recurso.
android:layout_toRightOf: Posiciona el borde izquierdo de esta vista a la derecha de la vista
especificada con un ID de recurso.
android:layout_toLeftOf: Posiciona el borde derecho de esta vista a la izquierda de la vista especificada
con un ID de recurso.
Estos son solo algunos ejemplos. Todos los atributos de diseño están documentados
en: https://developer.android.com/reference/android/widget/RelativeLayout.LayoutParams
El valor de cada propiedad de diseño es un valor booleano que habilita una posición de diseño
relativa al elemento RelativeLayout principal o un ID que hace referencia a otra vista en el diseño en
el que se debe posicionar la vista.
activity_main.xml
4.3. ScrollView
Un ScrollView es un ViewGroup que permite desplazarse por la jerarquía de vistas ubicada dentro de
él. ScrollView puede tener solo un hijo directo colocado dentro de él. Para agregar varias vistas
dentro de la vista de desplazamiento, haga que el hijo directo que agrega sea un ViewGroup, por
ejemplo, LinearLayout, y coloque vistas adicionales dentro de ese LinearLayout. Tal como se
muestra en el siguiente ejemplo:
Vistas Comunes
En este capítulo estudiaremos algunas de las vistas típicas que veremos en aplicaciones Android
como, por ejemplo, etiquetas (TextView), cajas de texto (EditText), Imágenes (ImageView), etc.
5.1. View
Esta clase representa el bloque de construcción básico para los componentes de la interfaz de
usuario (UI). Una vista (View) ocupa un área rectangular en la pantalla y es responsable del dibujo y
el manejo de eventos. View es la clase base para los widgets, que se utilizan para crear
componentes de interfaz de usuario interactivos (botones, campos de texto, etc.). La subclase
ViewGroup es la clase base para diseños, que son contenedores invisibles que contienen otras
Vistas (u otros ViewGroups) y definen sus propiedades de diseño.
Todas las vistas de una ventana están organizadas en un solo árbol. Puede agregar vistas desde el
código o especificando un árbol de vistas en uno o más archivos de diseño XML. Hay muchas
subclases especializadas de vistas que actúan como controles o son capaces de mostrar texto,
imágenes u otro contenido.
Una vez que haya creado un árbol de vistas, normalmente existen algunos tipos de operaciones
comunes que puede desear realizar:
5.2. TextView
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text_view_id"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/hello"/>
</LinearLayout>
Este ejemplo de código demuestra cómo modificar el contenido de la vista de texto definida en el
diseño XML anterior:
El EditText es un elemento de interfaz de usuario para ingresar y modificar texto. Cuando define un
widget de edición de texto, debe especificar el atributo R.styleable.TextView_inputType. Por ejemplo,
para la entrada de texto sin formato, establezca inputType en "text":
<EditText
android:id="@+id/plain_text_input"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:inputType="text"/>
La elección del tipo de entrada configura el tipo de teclado que se muestra, los caracteres aceptables
y la apariencia del texto de edición. Por ejemplo, si desea aceptar un número secreto, como un pin
único o un número de serie, puede establecer inputType en "numericPassword". Un inputType de
"numericPassword" da como resultado un texto de edición que solo acepta números, muestra un
teclado numérico cuando está enfocado y enmascara el texto que se ingresa para privacidad.
5.4. ImageView
Muestra recursos de imágenes, por ejemplo, Bitmap o recursos Drawable. ImageView también se
usa comúnmente para aplicar tintes a una imagen y manejar la escala de la imagen. El siguiente
fragmento XML es un ejemplo común del uso de ImageView para mostrar un recurso de imagen:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/my_image"
android:contentDescription="@string/my_image_description"/>
6. Botones
En este capitulo vamos a revisar algunos de los diferentes tipos de botones que Android proporciona
para construir nuestros tipos de interfaces. Primero revisaremos los botones típicos o push buttons
(botones que el usuario presiona y de inmediato realizan una acción estos son Button e
ImageButton). Luego revisaremos los botones de estado, es decir botones que realizan una acción
en función del estado al que el usuario lo cambia: Checkbox, RadioButton y Switch
6.1. Button
Un botón es un elemento de la UI que consta de texto o un ícono (o ambos, tanto texto como un
ícono) que comunica qué acción ocurre cuando el usuario lo toca (hace click).
Dependiendo de si desea un botón con texto, un icono o ambos, puede crear el botón en su diseño
de tres formas:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_text"
... />
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/button_icon"
android:contentDescription="@string/button_icon_desc"
... />
Con texto y un ícono, usando la clase Button con el atributo android: drawableLeft:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_text"
android:drawableLeft="@drawable/button_icon"
... />
Responder a eventos de clic
Cuando el usuario hace clic en un botón, el objeto Botón recibe un evento al hacer clic. Para definir
el controlador de eventos de clic para un botón, agregue el atributo android: onClick al elemento
<Button> en su diseño XML. El valor de este atributo debe ser el nombre del método al que desea
llamar en respuesta a un evento de clic. La actividad que aloja el diseño debe implementar el método
correspondiente. Por ejemplo, aquí hay un diseño con un botón que usa android: onClick:
<Button
android:id="@+id/btn_enviar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/boton_enviar"
android:onClick="enviarMensaje" />
Dentro de la actividad que aloja este diseño, el siguiente método maneja el evento de clic:
Usando un OnClickListener
También puede declarar el controlador de eventos de clic mediante programación en lugar de en un
diseño XML. Esto puede ser necesario si crea una instancia de la clase Button en tiempo de
ejecución o si necesita declarar el comportamiento del clic en una subclase de Fragment.
6.2. CheckBox
Las casillas de verificación (CheckBox) permiten al usuario seleccionar una o más opciones de un
conjunto. Por lo general, debe presentar cada opción de casilla de verificación en una lista vertical.
Para crear cada opción de casilla de verificación (CheckBox), creamos una casilla de verificación en
su diseño. Debido a que un conjunto de opciones de casilla de verificación permite al usuario
seleccionar varios elementos, cada casilla de verificación se administra por separado y debe registrar
un Listener de click para cada uno.
6.3. RadioButton
Los RadioButtons (También llamados botones de opción) permiten al usuario seleccionar una opción
de un conjunto. Debe usar botones de opción para conjuntos opcionales que se excluyan
mutuamente. Al crear una lista de opciones utilizando RadioButton debemos agruparlos dentro de un
RadioGroup. Al agruparlos, el sistema garantiza que solo se pueda seleccionar un botón de opción a
la vez:
<RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RadioButton android:id="@+id/radio_rojo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/rojo"
android:onClick="marcar"/>
<RadioButton android:id="@+id/radio_verde"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/verde"
android:onClick="marcar"/>
</RadioGroup>
Cuando el usuario selecciona uno de los botones de opción, el objeto RadioButton correspondiente
recibe un evento al hacer clic.
Para definir el controlador de eventos de clic para un botón, agregue el atributo android:onClick al
elemento <RadioButton> en su diseño XML. El valor de este atributo debe ser el nombre del método
al que desea llamar en respuesta a un evento de clic. La actividad que aloja el diseño debe
implementar el método correspondiente.
El método que declare en el atributo android:onClick debe tener una firma exactamente como se
muestra arriba. Específicamente, el método debe:
6.4. Switch
Un Switch es un widget que nos permite cambiar entre dos estados. El usuario puede arrastrar el
"pulgar" hacia adelante y hacia atrás para elegir la opción seleccionada, o simplemente tocar para
alternar como si fuera una casilla de verificación (CheckBox). La propiedad de texto controla el texto
que se muestra en la etiqueta del interruptor, mientras que el texto de apagado y encendido controla
el texto en el pulgar. De manera similar, los métodos textAppearance y setTypeface () relacionados
controlan el tipo de letra y el estilo del texto de la etiqueta, mientras que los métodos
switchTextAppearance y setSwitchTypeface () relacionados controlan el del pulgar.
Para detectar cuándo el usuario activa el switch, cree y asigne un objeto tipo
CompoundButton.OnCheckedChangeListener al método setOnCheckedChangeListener (). Por
ejemplo:
Switch sw =
findViewById(R.id.interruptor);
sw.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
// hacer algo si esta activado
} else {
// hacer algo sino esta activado
}
}
});
7. Intents
Un Intent es un objeto de mensajería que puedes usar para solicitar una acción de otro componente
de una aplicación. Si bien los intents facilitan la comunicación entre componentes de varias formas,
existen tres casos de uso principales:
Una Activity representa una única pantalla en una aplicación. Puedes iniciar una nueva
instancia de una Activity pasando un Intent a startActivity(). El Intent describe la actividad que
se debe iniciar y contiene los datos necesarios para ello. Si deseas recibir un resultado de la
actividad cuando finalice, llama a startActivityForResult(). La actividad recibe el resultado
como un objeto Intent separado en la devolución de llamada de onActivityResult() de la
actividad.
Iniciar un servicio
Un Service es un componente que realiza operaciones en segundo plano sin una interfaz de
usuario. Con Android 5.0 (nivel de API 21) y versiones posteriores, puedes iniciar un servicio
con JobScheduler. En las versiones anteriores a Android 5.0 (nivel de API 21), puedes iniciar
un servicio usando métodos de la clase Service. Puedes iniciar un servicio para realizar una
operación única (como descargar un archivo) pasando un Intent a startService(). El Intent
describe el servicio que se debe iniciar y contiene los datos necesarios para ello. Si el servicio
está diseñado con una interfaz cliente-servidor, podemos establecer un enlace con el servicio
de otro componente pasando un Intent a bindService().
Transmitiruna emisión
Una emisión es un aviso que cualquier aplicación puede recibir. El sistema transmite varias
emisiones de eventos, como cuando se inicia el sistema o comienza a cargarse el dispositivo.
Puedes transmitir una emisión a otras apps pasando una Intent a sendBroadcast() o
sendOrderedBroadcast().
El resto de esta página explica cómo funcionan las intents y cómo debes usarlas. Para
obtener información relacionada, consulta Cómo actualizar con otras aplicaciones y Cómo
compartir contenido.
7.1. Tipos de intents
Un filtro de intents es una expresión en el archivo de manifiesto de una aplicación que especifica el
tipo de intent que el componente podría recibir. Por ejemplo, declarar un filtro de intents para una
actividad permite que otras aplicaciones la inicien directamente con un tipo de intent específico.
Asimismo, si no declaras ningún filtro de intent para una actividad, esta solo se puede iniciar con una
intent explícita.
Un objeto Intent tiene información que el sistema Android usa para determinar qué componente debe
iniciar (como el nombre exacto del componente o la categoría que debe recibir la intent), además de
información que el componente receptor usa para realizar la acción correctamente (por ejemplo, la
acción que debe efectuar y los datos en los que debe actuar).
Esto es opcional, pero es la información clave que hace que una intent sea explícita, lo que significa
que la intent debe enviarse solamente al componente de la aplicación definido en el nombre del
componente. Sin un nombre de componente, la intent es implícita y el sistema decide qué
componente debe recibir la intent conforme la información restante que esta contiene (como la
acción, los datos y la categoría, que se describen a continuación). Por lo tanto, si necesitas iniciar un
componente específico en tu aplicación, debes especificar el nombre del componente.
Este campo de la Intent es un objeto ComponentName que puedes especificar con un nombre de
clase completamente calificado del componente de destino, incluido el nombre del paquete de la
aplicación, como com.example.ExampleActivity. Puedes establecer el nombre del componente con
setComponent(), setClass(), setClassName() o el constructor Intent.
Acción
Una string que especifica la acción genérica que se debe realizar (como ver o elegir).
En el caso de la intent de una emisión, es la acción que se produjo y que se está registrando. La
acción determina cuál es la estructura del resto de la intent, especialmente la información que se
incluye en los datos y extras.
Puedes especificar tus propias acciones para que las usen las intents en tu aplicación (o para que
las usen otras aplicaciones a fin de invocar componentes en tu aplicación); pero, usualmente, debes
especificar acciones constantes definidas por la clase Intent u otras clases de marcos de trabajo.
Estas son algunas acciones comunes para iniciar una actividad:
ACTION_VIEW: Usa esta acción en una intent con startActivity() cuando tengas información
que la actividad pueda mostrar al usuario, como una foto para ver en una app de galería o una
dirección para ver en una app de mapas.
ACTION_SEND: También se conoce como la intent de compartir y debes usarla en una intent
con startActivity() cuando tengas información que el usuario pueda compartir mediante otra
app, como una app de correo electrónico o intercambio social.
Consulta la referencia de la clase Intent para conocer más constantes que definen las acciones
genéricas. Otras acciones se definen en otras partes del marco de trabajo de Android, como en
Settings para las acciones que abren pantallas específicas en la aplicación de Configuración del
sistema.
Datos
El URI (un objeto Uri) que hace referencia a los datos en los que se debe realizar la acción o el tipo
de MIME de esos datos. El tipo de datos suministrado está generalmente determinado por la acción
de la intent. Por ejemplo, si la acción es ACTION_EDIT, los datos deben contener el URI del
documento que se debe editar.
Cuando creas una intent, es importante especificar el tipo de datos (su tipo de MIME) además de su
URI. Por ejemplo, una actividad que puede mostrar imágenes probablemente no sea capaz de
reproducir un archivo de audio aunque los formatos de URI sean similares. Especificar el tipo de
MIME de tus datos ayuda al sistema Android a encontrar el mejor componente para recibir tu intent.
Sin embargo, el tipo de MIME a veces se puede deducir del URI, especialmente cuando los datos
son un URI content:. Un URI content: indica que los datos se encuentran en el dispositivo y son
controlados por un ContentProvider, lo que hace que el sistema pueda ver el tipo de datos de MIME.
Para establecer solamente el URI de datos, llama a setData(). Para establecer solo el tipo de MIME,
llama a setType(). Si es necesario, puedes establecer ambos explícitamente con setDataAndType().
Categoría
Una string que contiene información adicional sobre el tipo de componente que la intent debe
manejar. En una intent, se puede incluir la cantidad deseada de descripciones de categorías, pero la
mayoría de las intents no requieren una categoría. Estas son algunas categorías comunes:
Extras
Pares clave-valor que contienen información adicional necesaria para completar la acción solicitada.
Al igual que algunas acciones usan tipos particulares de URI de datos, algunas acciones también
usan extras específicos.
Puedes agregar datos adicionales con varios métodos putExtra(), cada uno de los cuales acepta dos
parámetros: el nombre de la clave y el valor. También puedes crear un objeto Bundle con los datos
adicionales y, luego, insertar el Bundle en la Intent con putExtras().
Por ejemplo, al crear una intent para enviar un correo electrónico con ACTION_SEND, puedes
especificar el destinatario para con la clave EXTRA_EMAIL y el asunto con la clave
EXTRA_SUBJECT.
Indicadores
Los indicadores se definen en la clase Intent que funciona como metadatos de la intent. Los
indicadores pueden indicar al sistema Android cómo iniciar una actividad (por ejemplo, a qué tarea
debe pertenecer) y cómo tratarla después de que se inicia (por ejemplo, si pertenece a la lista de
actividades recientes).
Una intent implícita especifica una acción que puede invocar cualquier aplicación en el dispositivo
que puede realizar la acción. El uso de una intent implícita es útil cuando la aplicación no puede
realizar la acción, pero otras aplicaciones probablemente sí, y quieres que el usuario elija qué
aplicación usar.
Por ejemplo, si tienes contenido que quieres que el usuario comparta con otras personas, crea una
intent con la acción ACTION_SEND y agrega extras que especifiquen el contenido para compartir.
Cuando llamas a startActivity() con esa intent, el usuario puede elegir una aplicación mediante la cual
compartir el contenido.
Una intent explícita es una intent que se usa para iniciar un componente específico de la aplicación,
como una actividad o un servicio particular en la aplicación. Para crear una intent explícita, define el
nombre de componente del objeto Intent (todas las otras propiedades de la intent son opcionales).