En este artículo veremos que son Layouts y Views para el diseño de la interfaz de usuario (UI) de una aplicación Android.
Crearemos una pequeña aplicación paso a paso para comprender como definir nuestros diseños, relacionarlos con el código Java y manejar eventos disparados por el usuario al interactuar con la interfaz.
Parsing XML para generar código Java en Android
Cuando vimos la estructura de un proyecto en Android, hablamos sobre los recursos de la carpeta layout, los cuales permitían crear la interfaz de una actividad en Android.
Lee también ¿Qué hay dentro de la carpeta layout?
Un momento…. ¿qué significa eso?, ¿no se supone que crear la interfaz es responsabilidad del código Java?
¡Claro que sí!, crear una interfaz en Android es similar a cuando se crea una interfaz con los paquetes AWT o SWING de Java. ¿Recuerdas que primero se declaraban objetos que manejaran los controles, luego se añadían a un contenedor, se configuraban todos sus atributos y al final se añadían los eventos que podrían recibir?
A ese estilo de diseño de interfaz se le llama Diseño de interfaz de usuario programático y es lo que cualquier programador en primera instancia se imaginaria que tiene que hacer en una aplicación Android.
Pero la realidad es otra. Los proyectos en Android tienen la capacidad de implementar el poder de XML para reducir la complejidad de diseño de interfaz, permitiéndonos declarar la estructura en un simple archivo definido por elementos muy intuitivos.
Estos archivos luego son analizados con una librería de Parsing XML para generar automáticamente todo el código Java necesario a partir de una estructura predefinida.
Lee también Tutorial Básico del Lenguaje XML
Esta característica ubica el diseño de interfaz de usuario en un alto nivel, evitándonos la escritura de código abrumador. A este tipo de diseño se le llama Diseño de interfaz de usuario declarativo.
La idea es usar el estilo declarativo para crear la forma de la interfaz y luego usar el estilo programático para implementar los eventos sobre la interfaz. La combinación de ambas formas desata un poder de flexibilidad increíble.
Obviamente es posible crear todo el código Java para desarrollar una aplicación Android, pero este no será el enfoque de este artículo ni de ningún otro. Cabe aclarar que en algunos momentos, sobre todo cuando se deseen añadir elementos de la interfaz en tiempo de ejecución, será necesario usar la definición de la interfaz de forma programática, pero casi siempre nos referiremos al diseño declarativo.
¿Qué es un Layout?
Un layout es un objeto que representa el espacio contenedor de todas las vistas (Views) dentro de la actividad. En él se define la estructura y el orden de los elementos para que el usuario pueda interactuar con la interfaz. Lógicamente se representan con subclases Java que heredan de la clase ViewGroup.
Para definirlos escribiremos un documento XML que contenga como nodo raíz algún tipo de layout y luego incluir en su interior los elementos hijos. Android Studio permite generar automáticamente el código de los archivos XML a través del panel de Diseño.
¿Qué tipos de Layouts existen en el Desarrollo Android?
Existen varios y su uso depende de la necesidad de cada persona. Veamos la definición de los más populares y fáciles de usar:
LinearLayout
: El Linear Layout es el más sencillo. Dentro de él los elementos son ubicados en forma linear a través de columnas o filas. Posee un atributo que permite modificar su orientación, ya sea para presentar los elementos horizontal o Verticalmente.WebView
: Este View fue creado para mostrar el contenido con formato web.RelativeLayout
: Este es el layout más recomendado a usar, ya que los componentes dentro de él se pueden organizar de forma relativa entre sí.
Observa la siguiente ilustración:
Frecuentemente usaremos el Relative Layout para nuestros proyectos debido a la referencia relativa que podemos asignar a los componentes hijos. Cuando digo relativa me refiero a que no expresaremos la ubicación de los componentes de esta forma:
«El botón OK estará ubicado en el punto (200,120) del layout y sus dimensiones son 200×30 dp«
A esa definición de atributos se le llama definición absoluta, y describe con pelos y señales cuales son las medidas numéricas para el View.
A diferencia de esa declaración, dentro de un RelativeLayout usaremos expresiones como la siguiente:
«El botón OK estará ubicado a la izquierda del extremo derecho del TextView 2 y sus dimensiones serán ajustadas al padre«
¿Qué significa eso?, quiere decir que no importa de qué tamaño sea la pantalla o que densidad maneje, el botón se ajustará relativamente a las condiciones que se le han impuesto, lo cual permite una mejor experiencia para distintos usuarios sin importar las características de su dispositivo.
Usar Adaptadores para elementos dinámicos
Un adaptador es una interfaz entre la forma de un layout y los datos que este contendrá. Debido a que los elementos se deben añadir en tiempo de ejecución, un adaptador viene preparado para conectar la información de forma secuencial y organizada para controlar dinámicamente las instrucciones. Luego estudiaremos más a fondo este tópico.
Inflar código Java para crear Aplicaciones Android
A menudo te encontraras con el término Inflate (inflar) en la programación Android. Inflar código Java significa parsear uno a uno los elementos de un archivo layout XML, para generar el código Java necesario, y luego agregar los objetos a la memoria del Heap.
Quien se encarga de este trabajo es el método setContentView() de la clase Activity. El analiza el archivo XML, traduce a objetos cada componente, le asigna los atributos, establece contenedores y todas las relaciones padre e hijo necesarias. Este método lo usaremos en la sección onCreate() de cada actividad.
Miremos el código de MyActivity.java del proyecto Test:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); }
Como ves el método onCreate() usa setContentView() para inflar el contenido desde el archivo de recurso activity_my.xml. La siguiente ilustración muestra de forma simple el proceso de parsing:
El parámetro de entrada savedInstance de create() es un objeto de tipo Bundle, el cual recibe datos externos que pueden ser de utilidad para el comportamiento de la Actividad. A menudo se usa para coordinar el cambio de posición de pantalla de Landscape a Portrait o viceversa.
También es de utilidad cuando una actividad se está reactivando. En los próximos artículo veremos más en detalle este tema.
¿Qué es un View?
Es un componente que permite controlar la interacción del usuario con la aplicación. Estos son muy similares a los controles SWING de Java, como Labels, Buttons, TextFields, Checkboxes, etc. Los Views son organizados dentro de los Layouts para que el usuario comprenda los objetivos de la actividad.
Atributos De Un View
Veamos los propósitos de los atributos usados con mayor frecuencia al diseñar un layout:
layout:height
: Representa la dimensión de longitud vertical de un View. Puedes asignarle valores absolutos en dps, si dependiendo de las métricas de diseño que tengas ó usar los valoresmatch_parent
ywrap_content
. El primero ajusta la dimensión a las medidas del contenedor padre y el segundo lo ajusta al contenido del View.
layout:width
: Representa el ancho de un view.layout:layout_margin
: Especifica las márgenes del view con respecto a otros componentes. Podemos definir los valores individualmente para la margen izquierda, derecha, superior e inferior. O si deseas puedes especificar un mismo valor para todos los margenes.layout:alignComponent
: Indica la adyacencia de las margenes entre componentes. Por ejemplo, esto permitiría ubicar el margen izquierdo de un botón justo al lado del margen izquierdo de otro botón. Si en algún momento el botón cambia entonces su compañero cambiará con él. Este atributo muestra el poder de un Relative layout.layout:alignParent
: Con este atributo especificamos que un view estará ubicado hacia uno de los lados de su padre.layout:centerInParent
: Permite centrar horizontal y verticalmente un View con respecto a su padre.id
: Es un nombre que diferencia los views entre si. Es de utilidad cuando vayamos a referenciar los controles en el código Java. Así que elige nombres representativos y de fácil interpretación.
¿Y cuando programamos?
¡Justo en este instante!
Crearemos una pequeña aplicación con un Relative Layout y los siguientes tres views: un EditText, un TextView, y un Button. El objetivo general es que el usuario digite algún texto en la caja de texto y al presionar el botón la aplicación muestre la cantidad de caracteres tiene.
Veamos los pasos a seguir:
Paso 1: Crear un nuevo proyecto
Crea un nuevo proyecto o reutiliza el proyecto Test que habíamos creado en los artículos iníciales.
Paso 2: Abrir el archivo Layout
Abre el archivo de recursos que guarde el layout de tu actividad principal. En mi caso es activity_my.
Paso 3: Arrastrar y Soltar Views
Presiona la pestaña «Design» en la parte inferior del editor:
La herramienta que se acaba de visualizar es el panel de diseño de interfaz de Android Studio.
- En la parte central se encuentra un lienzo que muestra la interfaz actual en un dispositivo.
- En su parte superior tenemos una barra que permite configurar el dispositivo elegido para la previsualización, herramientas de zoom, la versión de android a visualizar, el modo táctil (Landscape o Portrait), etc.
- A la izquierda veremos la paleta o estante de views para construir la actividad.
- Al lado derecho se encuentra el Component Tree o árbol de componentes, en esta ventana se representa en forma jerárquica la relación de nuestros views.
- Debajo del Component Tree está el panel de propiedades. En el veremos todos los atributos que tiene un view seleccionado, los cuales podemos editar con facilidad.
A continuación arrastraremos al layout el siguiente un Multiline Text de la sección Text Fields. Ubícalo en la parte superior y centrado horizontalmente, de tal forma que te aparezcan los tooltips que se muestran en la ilustración derecha.
¿Qué es un EditText?
El view que acabamos de agregar pertenece a la familia de los TextFields. Un TextField es un control que espera la entrada de texto del usuario, como por ejemplo su nombre, teléfono y datos que sean útiles para la aplicación.
El que acabamos de agregar es de tipo MultiLine, lo que significa que puede recibir varios renglones de caracteres en su contenido. También hay otros tipos que solo reciben números, o texto referente a un correo electrónico, etc.
A pesar de que en diseñador se llame TextField en Java es representado por la subclase EditText y en XML con el elemento <EditText>.
¿Qué es un Button?
Como su nombre lo indican se refiere a un botón. Como usuarios hemos visto y clickeado millones de veces a estos amigos. Su propósito es ejecutar una acción que permita al usuario completar un proceso, confirmar una acción, ejecutar más características de la aplicación, etc.
En Java lo representaremos con la subclase Button y su equivalente en XML es el elemento <Button>.
Ahora pasaremos a agregar un botón. Utiliza las mismas características de ubicación mostradas en la ilustración de la parte derecha.
¿Qué es un Medium Text?
Es un texto estático de tamaño promedio en la interfaz de usuario. Su propósito es informar o guiar el recorrido en la aplicación. Como ves, adicionalmente hay más texts en el panel: Small (Pequeño), Large (Gigante) y Plain Text (Texto plano). Si necesitas una referencia adicional, podrías comparar el Medium Text con un Label de SWING.
El medium Text se representa con la subclase de Java TextView, lo de «medium» simplemente fue posible editando un atributo que define el tamaño de la fuente. Su equivalente XML es el componente <TextView>.
De nuevo arrastra y posiciona como indican las siguientes ilustraciones:
Si todo salió bien el diseño final te habrá quedado similar a este:
Paso 4: Configurar atributos de los Views
Lo que sigue es asignar los IDs, las dimensiones y las posiciones de nuestros tres componentes en el layout.
Para ello selecciona primero el textview en el Component Tree. Luego dirígete a sus propiedades, busca el atributo id y emplea un nombre significativo para nuestro componente. En mi caso yo le he asignado main_textview.
Luego haremos lo mismo para los demás views y veremos cómo cambian sus nombres en el Component Tree.
Nuestro editText fue configurado inicialmente para que su límite superior estuviera alineado al límite superior del Relative Layout. Por eso si vemos sus propiedades podremos ver el atributo layout:alignParent seleccionado en la opción «top«.
Al seleccionar el botón veremos que está ubicado en la parte inferior del editText, con una margen superior de 41dp y alineado horizontalmente. Estas características se representan con los atributos layout:margin , layout:alignComponent y layout:centerInParent.
Nota que align_component se visualiza como un conjunto de opciones donde podemos especificar las adyacencias entre componentes. Si te fijas, el botón tiene alineado a su límite superior el límite inferior del editText(top:bottom).
El textView tiene una alineación central vertical tanto como horizontal. Si te fijas el valor de centerInParent es both que significa que se centrará en las dos dimensiones.
Ahora intenta asignarle a su límite superior el límite inferior del botón y además añádele un margen superior de 41dp.
Seguido modificaremos un atributo especial llamado hint del editText. Hint indica a través de un texto informativo, el tipo de texto que el edittext puede recibir. En mi caso le voy a poner «Mensaje».
Lo siguiente es modificar los atributos text del textView y el botón, asignando una cadena vacía al primero y la cadena «Procesar» al segundo:
Todo lo que hemos realizado hasta el momento pudo haberse especificado a través de la pestaña Text del Editor. Esta pestaña muestra la descripción XML sobre lo que acabamos de construir en la pestaña de Diseño.
Si vas a la pestaña Text tendrías algo similar a esto:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MyActivity"> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:inputType="textMultiLine" android:ems="10" android:id="@+id/main_editText" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:hint="Mensaje" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/main_button" android:layout_below="@+id/main_editText" android:layout_centerHorizontal="true" android:layout_marginTop="41dp" android:text="Procesar" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:id="@+id/main_textview" android:layout_centerVertical="true" android:layout_centerHorizontal="true" android:layout_below="@+id/main_button" android:layout_marginTop="41dp" /> </RelativeLayout>
Este archivo muestra los componentes de cada View y los atributos que le hemos asignado (también hay otros atributos configurados intrínsecamente por Android Studio).
Paso 5: Añadir las cadenas relevantes al archivo strings.xml
Preguntémonos lo siguiente…
¿Qué cadenas son estáticas y no se modificarán a lo largo de la ejecución de la aplicación?
A mi parecer el texto del botón sería muy importante al igual que el Hint del edittext. El texto del textview está destinado a ser dinámico así que lo descartaría.
Una vez definido esto, vamos a agregar estas cadenas al archivo strings.xml. Para ello vamos al panel de propiedades y buscamos el atributo text del botón y hint del edittext. Presionaremos el botón «…«.
La ventana que se acaba de desplegar permite que elijamos y naveguemos a través de todos los recursos del proyecto en la pestaña «Project». Existe otra pestaña llamada «System», pero sus utilidades las veremos luego.
Ya que el recurso que necesitamos usar aun no existe, debemos crearlo. Así que haremos clic en «New Resource» y seleccionaremos «New String Value…».
A continuación nos aparecerá un formulario con dos campos que describen al string, Resource name y Resource content. El primero hace referencia al nombre del recurso, el segundo al contenido del string. En mi caso como nombre le asignaré main_button al texto del botón. Y para el hint del edittext usaré «main_hint».
En los contenidos ubica el texto que teníamos al inicio.
Guardamos y el recurso estará creado. Si deseas abre el archivo strings.xml y comprueba la existencia de los nodos que acabas de crear.
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Test</string> <string name="action_settings">Settings</string> <string name="hint">Mensaje</string> <string name="button">Procesar</string> </resources>
Naturalmente otra forma de añadir los nodos a strings.xml es de forma manual si en algún momento deseas hacerlo.
Paso 6: Programar los eventos
Ya hemos acabo con toda la parte de diseño e interfaz. Lo que sigue es programar lo que queremos que la aplicación realice si el botón es presionado. El algoritmo sería algo como:
¿Botón presionado?:
SI
-Obtener valor del editText
-Poner valor del editText en el textView
¿Cómo saber si un botón fue presionado?
Simplemente usaremos una Interfaz que escuche los taps del usuario en un view. Recuerda que los listeners o escuchas en español, son Interfaces implementadas en Java para participar en la recepciones de eventos. Bueno supongo que eso ya lo sabes.
Para los botones usaremos una escucha especial llamada OnClickListener. Solo basta añadir una relación entre nuestro botón y la escucha para reportar el evento.
Pero… ¿Cómo accedemos al botón?
Debes crear un objeto del tipo Button para guardar la instancia del botón.
private Button boton;
Ahora dentro del método callback onCreate() llamaremos al método de la clase Activity findViewById(). Este método retorna en los views que se encuentran en el layout a través de una referencia.
Como devuelve un objeto de tipo View, es necesario realizar el casting pertinente.Veamos:
boton = (Button)findViewById(R.id.main_button);
La referencia del botón se obtiene a través del atributo id de la clase R.
Con ese proceso claro pasamos a codificar de forma análoga con los otros dos Views:
boton = (Button)findViewById(R.id.main_button); edit = (EditText)findViewById(R.id.main_editText); text = (TextView)findViewById(R.id.main_textview);
Lo que sigue es anidar una nueva instancia de la escucha al botón con el método de la clase Button setOnClickListener():
boton.setOnClickListener( new OnClickListener() { public void onClick(View view){ //Acciones } });
OnClickListener permite sobreescribir su método de escucha de taps onClick(). Dentro de él agregaremos las acciones que se ejecutarán al presionar el botón. El parámetro de entrada view es el view que fue presionado, el cual no es utilizado en este ejemplo.
¿Qué contendrá el listener?
Las acciones que establecimos en nuestro algoritmo: Obtener el valor del edit text y el setearlo en el viewtext.
Veamos:
String mensaje = edit.getText().toString(); text.setText(String.valueOf(mensaje.length()));
Lo que acabamos de hacer es una fácil transferencia del mensaje desde el editText hasta el textView. Para ello usamos los métodos getters y setters del atributo text que ambos poseen. El método getText() de EditText no retorna en la cadena como tal del componentes, si no en un objeto del tipo Editable, por ello luego casteamos a String con toString().
Este sería el código completo:
public class MyActivity extends Activity { private Button boton; private EditText edit; private TextView text; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); boton = (Button)findViewById(R.id.main_button); edit = (EditText)findViewById(R.id.main_editText); text = (TextView)findViewById(R.id.main_textview); boton.setOnClickListener( new OnClickListener() { public void onClick(View view){ String mensaje = edit.getText().toString(); text.setText(String.valueOf(mensaje.length())); } }); } }
Finalmente ejecuta el proyecto en Android Studio para obtener el siguiente resultado:
Conclusión
Quienes inician en el Desarrollo Android puede que se les haga difícil entender el concepto de «inflar» views. No obstante la práctica constante interiorizará este concepto.
Crear diseños de interfaz para tus aplicaciones requiere aprendizaje continuo. También exige que comprendas los patrones de diseño y respetes los lineamientos.
El paso a seguir es comprender el funcionamiento de fragmentos dentro de las aplicaciones Android.
Únete Al Discord De Develou
Si tienes problemas con el código de este tutorial, preguntas, recomendaciones o solo deseas discutir sobre desarrollo Android conmigo y otros desarrolladores, únete a la comunidad de Discord de Develou y siéntete libre de participar como gustes. ¡Te espero!