The Android Developer Challenge is back! Submit your idea before December 2.

Diseños   Part of Android Jetpack.

Un diseño define la estructura de una interfaz de usuario en tu aplicación, como, por ejemplo, en una actividad. Todos los elementos del diseño se crear 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, tal como se muestra en la figura 1.

Figura 1: Ilustración de una jerarquía de vistas, que define un diseño de IU

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 ConstraintLayout.

Puedes declarar un diseño de dos maneras:

  • 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. Tu aplicación puede crear objetos View y ViewGroup (y manipular sus propiedades) de forma programática.

Declarar tu IU en XML te permite separar la presentación de tu 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 (consulta más adelante Cómo admitir diferentes tamaños de pantalla).

El marco de trabajo de Android te ofrece la flexibilidad de usar uno de estos métodos o ambos para crear la IU de tu aplicación. Por ejemplo, puedes declarar los diseños predeterminados de tu aplicación en XML y, luego, modificar el diseño durante el tiempo de ejecución.

Sugerencia: Para depurar tu diseño durante el tiempo de ejecución, usa la herramienta Layout Inspector.

Escribe en XML

Al usar vocabulario XML de Android, puedes crear rápidamente diseños de IU y de los elementos de pantalla que contienen, de la misma manera que creas páginas web en HTML, con una serie de elementos anidados.

Cada archivo de diseño debe contener exactamente un elemento raíz, que debe ser un objeto View o ViewGroup. Una vez que hayas definido el elemento raíz, puedes agregar widgets u objetos de diseño adicionales como elementos secundarios para crear gradualmente una jerarquía de vistas que defina tu diseño. Por ejemplo, aquí te mostramos un diseño XML que usa un LinearLayout vertical para incluir una TextView y un Button:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical" >
    <TextView android:id="@+id/text"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Hello, I am a TextView" />
    <Button android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello, I am a Button" />
</LinearLayout>

Después de declarar tu diseño en XML, guarda el archivo con la extensión .xml en el directorio res/layout/ de tu proyecto de Android para que pueda compilarse correctamente.

Puedes encontrar más información acerca de la sintaxis para un archivo de diseño XML en el documento Recursos de diseño.

Carga el recurso XML

Cuando compilas tu aplicación, cada archivo de diseño XML se compila en un recurso View. Debes cargar el recurso de diseño desde el código de tu aplicación, en la implementación de devolución de llamada Activity.onCreate(). Para eso, llama a setContentView() pasando la referencia a tu recurso de diseño en forma de R.layout.layout_file_name. Por ejemplo, si tu diseño XML se guarda como main_layout.xml, los cargarás para tu actividad de la misma manera:

Kotlin

fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_layout)
}

Java

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_layout);
}

El marco de trabajo de Android llama al método de devolución de llamada onCreate() en tu actividad cuando esta se lanza (consulta el artículo acerca de los ciclos de vida en el documento Actividades).

Atributos

Cada objeto View y ViewGroup admite su propia variedad de atributos XML. Algunos atributos son específicos de un objeto View (por ejemplo, TextView admite el atributo textSize), aunque estos atributos también son heredados por cualquier objeto View que pueda extender esta clase. Algunos son comunes para todos los objetos View, porque se heredan de la clase raíz View (como el atributo id). Otros atributos se consideran "parámetros de diseño" y son atributos que describen ciertas orientaciones de diseño del objeto View, tal como lo define el objeto principal ViewGroup de ese objeto.

ID

Cualquier objeto View puede tener un ID entero asociado para identificarse de forma única dentro del árbol. Cuando se compila la aplicación, se hace referencia a este ID como un número entero, pero el ID se asigna normalmente en el archivo XML de diseño como una string del atributo id. Este es un atributo XML común para todos los objetos View (definido por la clase View) y lo utilizarás muy a menudo La sintaxis de un ID dentro de una etiqueta XML es la siguiente:

android:id="@+id/my_button"

El símbolo arroba (@) al comienzo de la string indica que el analizador de XML debe analizar y expandir el resto de la string de ID e identificarla como un recurso de ID. El símbolo más (+) significa que es un nuevo nombre de recurso que se debe crear y agregar a nuestros recursos (en el archivo R.java). El marco de trabajo de Android ofrece otros recursos de ID. Al hacer referencia a un ID de recurso de Android, no necesitas el símbolo más, pero debes agregar el espacio de nombres de paquete android de la siguiente manera:

android:id="@android:id/empty"

Con el espacio de nombres de paquete android establecido, ahora hacemos referencia a un ID de la clase de recursos android.R, en lugar de la clase de recursos local.

Para crear vistas y hacer referencia a ellas desde la aplicación, puedes seguir este patrón común:

  1. Definir una vista o un widget en el archivo de diseño y asignarle un ID único:
    <Button android:id="@+id/my_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/my_button_text"/>
    
  2. Luego, crear una instancia del objeto View y capturarla desde el diseño (generalmente en el método onCreate()):

    Kotlin

    val myButton: Button = findViewById(R.id.my_button)
    

    Java

    Button myButton = (Button) findViewById(R.id.my_button);
    

Definir ID para objetos View es importante cuando se crea un RelativeLayout. En un diseño relativo, las vistas del mismo nivel pueden definir su diseño en función de otra vista del mismo nivel, que se identifica con un ID único.

No es necesario que un ID sea único en todo el árbol, pero debe ser único dentro de la parte del árbol en la que estás buscando (que a menudo puede ser el árbol completo, por lo que es mejor que, en lo posible, sea siempre único).

Parámetros de diseño

Los atributos de diseño XML denominados layout_something definen parámetros de diseño para el objeto View que son adecuados para el objeto ViewGroup en el que reside.

Cada clase ViewGroup implementa una clase anidada que extiende ViewGroup.LayoutParams. Esta subclase contiene tipos de propiedad que definen el tamaño y la posición de cada vista secundaria, según resulte apropiado para el grupo de vistas. Como puedes ver en la figura 2, el grupo de vistas principal define parámetros de diseño para cada vista secundaria (incluido el grupo de vistas secundario).

Figura 2: Visualización de una jerarquía de vistas con parámetros de diseño asociados con cada vista

Ten en cuenta que cada subclase LayoutParams tiene su propia sintaxis para configurar valores. Cada elemento secundario debe definir LayoutParams adecuados para su elemento primario, aunque también puede definir diferentes LayoutParams para sus propios elementos secundarios.

Todos los grupos de vistas incluyen un ancho y una altura (layout_width y layout_height), y cada vista debe definirlos. Muchos LayoutParams también incluyen márgenes y bordes opcionales.

Puedes especificar el ancho y la altura con medidas exactas, aunque probablemente no quieras hacerlo con mucha frecuencia. Generalmente, usarás una de estas constantes para establecer el ancho o la altura:

  • wrap_content indica a tu vista que modifique su tamaño conforme a los requisitos de este contenido.
  • match_parent indica a tu vista que se agrande tanto como lo permita su grupo de vistas principal.

En general, no se recomienda especificar el ancho ni la altura de un diseño con unidades absolutas como píxeles. En cambio, el uso de medidas relativas como unidades de píxeles independientes de densidad (dp), wrap_content o match_parent es un mejor enfoque, ya que ayuda a garantizar que tu aplicación se muestre correctamente en dispositivos con pantallas de diferentes tamaños. Los tipos de medidas aceptados se definen en el documento Recursos disponibles.

Posición del diseño

La geometría de una vista es la de un rectángulo. Una vista tiene una ubicación, expresada como un par de coordenadas izquierda y superior, y dos dimensiones, expresadas como un ancho y una altura. La unidad para la ubicación y las dimensiones es el píxel.

Es posible recuperar la ubicación de una vista al invocar los métodos getLeft() y getTop(). El primero muestra la coordenada izquierda, o X, del rectángulo que representa la vista. El segundo muestra la coordenada superior, o Y, del rectángulo que representa la vista. Ambos métodos devuelven la ubicación de la vista respecto de su elemento primario. Por ejemplo, cuando getLeft() muestra 20, significa que la vista se encuentra a 20 píxeles a la derecha del borde izquierdo de su elemento primario directo.

Además, se ofrecen varios métodos convenientes para evitar cálculos innecesarios, denominados getRight() y getBottom(). Estos métodos muestran las coordenadas de los bordes derecho y superior del rectángulo que representa la vista. Por ejemplo, llamar a getRight() es similar al siguiente cálculo: getLeft() + getWidth().

Tamaño, relleno y márgenes

El tamaño de una vista se expresa con un ancho y una altura. En realidad, una vista tiene dos pares de valores de ancho y altura.

El primer par se conoce como ancho medido y altura medida. Estas dimensiones definen cuán grande quiere ser una vista dentro de su elemento primario. Las dimensiones medidas se pueden obtener llamando a getMeasuredWidth() y a getMeasuredHeight().

El segundo par se conoce simplemente como ancho y altura, o, algunas veces, ancho de dibujo y altura de dibujo. Estas dimensiones definen el tamaño real de la vista en la pantalla en el momento de dibujarlas y después del diseño. Estos valores pueden ser diferentes del ancho y la altura medidos, pero no necesariamente. El ancho y la altura se pueden obtener llamando a getWidth() y getHeight().

Para medir estas dimensiones, una vista considera su relleno. El relleno se expresa en píxeles para las partes izquierda, superior, derecha e inferior de la vista. El relleno se puede usar para desplazar el contenido de la vista una determinada cantidad de píxeles. Por ejemplo, un relleno izquierdo de 2 empuja el contenido de la vista 2 píxeles hacia la derecha del borde izquierdo. El relleno se puede ajustar usando el método setPadding(int, int, int, int) y se puede consultar llamando a getPaddingLeft(), getPaddingTop(), getPaddingRight() y getPaddingBottom().

Si bien una vista puede definir un relleno, no proporciona ningún tipo de compatibilidad para márgenes. No obstante, los grupos de vistas sí la proporcionan. Consulta ViewGroup y ViewGroup.MarginLayoutParams para obtener más información.

Para obtener más información acerca de las dimensiones, lee Valores de dimensión.

Diseños comunes

Cada subclase de la clase ViewGroup proporciona una manera única de mostrar las vistas que anidas en ella. Aquí te mostramos algunos de los tipos de diseño más comunes integrados en la plataforma Android.

Nota: Si bien puedes anidar uno o más diseños dentro de otro diseño para crear la presentación de la IU, debes esforzarte por mantener tu jerarquía de diseño lo más sencilla posible. Tu diseño se procesa más rápido si tiene menos diseños anidados (una jerarquía de vistas ancha es mejor que una de vista profunda).

Diseño lineal

Un diseño que organiza sus elementos secundarios en una sola fila horizontal o vertical. Si la longitud de la ventana supera la de la pantalla, crea una barra de desplazamiento.

Diseño relativo

Te permite especificar la ubicación de los objetos secundarios en función de ellos mismos (el objeto secundario A a la izquierda del objeto secundario B) o en función del elemento primario (alineado con la parte superior del elemento primario).

Vista web

Muestra páginas web.

Creación de diseños con un adaptador

Cuando el contenido de tu diseño sea dinámico o no sea predeterminado, puedes usar un diseño con la subclase AdapterView para completar el diseño con vistas durante el tiempo de ejecución. Una subclase de la clase AdapterView usa un Adapter para enlazar datos con su diseño. Adapter se comporta como intermediario entre la fuente de datos y el diseño AdapterView; Adapter recupera los datos (de una fuente como una matriz o una consulta a la base de datos) y convierte cada entrada en una vista que puedes agregar al diseño AdapterView.

Los diseños comunes respaldados por un adaptador incluyen:

Vista de lista

Muestra una sola lista de columnas desplazable.

Vista de cuadrícula

Muestra una cuadrícula desplazable de columnas y filas.

Relleno de una vista del adaptador con datos

Puedes completar una AdapterView, como ListView o GridView, enlazando la instancia AdapterView con un Adapter, que recupera datos de una fuente externa y crea una View que representa cada entrada de datos.

Android proporciona varias subclases de Adapter que resultan útiles para recuperar diferentes tipos de datos y generar vistas para una AdapterView. Los dos adaptadores más comunes son los siguientes:

ArrayAdapter
Usa este adaptador cuando la fuente de datos sea una matriz. Según la configuración predeterminada, ArrayAdapter crea una vista para cada elemento de la matriz llamando a toString() en cada elemento y disponiendo los contenidos en una TextView.

Por ejemplo, si hay una matriz de strings que deseas visualizar en una ListView, inicializa un nuevo ArrayAdapter usando un constructor para especificar el diseño de cada string y la matriz de strings:

Kotlin

val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray)

Java

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
        android.R.layout.simple_list_item_1, myStringArray);

Los argumentos para este constructor son los siguientes:

  • El Context de tu app
  • El diseño que contiene una TextView para cada string de la matriz
  • La matriz de strings

Luego, simplemente, llama a setAdapter() en tu ListView:

Kotlin

val listView: ListView = findViewById(R.id.listview)
listView.adapter = adapter

Java

ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);

Para personalizar el aspecto de cada elemento, puedes anular el método toString() de los objetos de tu matriz. Como alternativa, si deseas crear una vista para cada elemento que no sea una TextView (por ejemplo, si deseas una ImageView para cada elemento de la matriz), extiende la clase de ArrayAdapter y anula getView() a fin de mostrar el tipo de vista que desees para cada elemento.

SimpleCursorAdapter
Usa este adaptador cuando tus datos provengan de un Cursor. Cuando uses SimpleCursorAdapter, debes especificar un diseño para cada fila en el Cursor y qué columnas de Cursor se deben insertar en qué vistas del diseño. Por ejemplo, si deseas crear una lista de nombres y números de teléfono de personas, puedes realizar una consulta que muestre un Cursor con una fila para cada persona y columnas para los nombres y los números. Luego, crearás una matriz de strings que especifique las columnas del Cursor que desees en el diseño para cada resultado y una matriz con valores enteros que especifique las vistas correspondientes en las que se deba colocar cada columna:

Kotlin

val fromColumns = arrayOf(ContactsContract.Data.DISPLAY_NAME,
                          ContactsContract.CommonDataKinds.Phone.NUMBER)
val toViews = intArrayOf(R.id.display_name, R.id.phone_number)

Java

String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
                        ContactsContract.CommonDataKinds.Phone.NUMBER};
int[] toViews = {R.id.display_name, R.id.phone_number};

Cuando crees una instancia del SimpleCursorAdapter, pasa el diseño que se debe usar para cada resultado, el Cursor que contiene los resultados y estas dos matrices:

Kotlin

val adapter = SimpleCursorAdapter(this,
        R.layout.person_name_and_number, cursor, fromColumns, toViews, 0)
val listView = getListView()
listView.adapter = adapter

Java

SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
        R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
ListView listView = getListView();
listView.setAdapter(adapter);

Luego, el SimpleCursorAdapter crea una vista para cada fila en el Cursor usando el diseño proporcionado al insertar cada elemento fromColumns en la vista toViews correspondiente.

.

Si durante el ciclo de vida de tu aplicación cambias los datos subyacentes que lee tu adaptador, debes llamar a notifyDataSetChanged(). Esto le notificará a la vista anexada que se modificaron los datos y que debe actualizarse.

Cómo manejar eventos de clic

Puedes responder a eventos de clic en cada elemento de una AdapterView al implementar la interfaz AdapterView.OnItemClickListener. Por ejemplo:

Kotlin

listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
    // Do something in response to the click
}

Java

// Create a message handling object as an anonymous class.
private OnItemClickListener messageClickedHandler = new OnItemClickListener() {
    public void onItemClick(AdapterView parent, View v, int position, long id) {
        // Do something in response to the click
    }
};

listView.setOnItemClickListener(messageClickedHandler);

Recursos adicionales

Los diseños se usan en la aplicación de demostración de Sunflower.