La interfaz de usuario en Android se construye con archivos XML que describen el diseño visual y se conectan al código Kotlin mediante View Binding.
Cada pantalla de tu app Android es un layout: un archivo XML que define qué elementos aparecen y cómo se organizan. View Binding es el mecanismo oficial para acceder a esos elementos desde Kotlin sin riesgos de errores.
¿Qué es un layout XML en Android?
Un layout XML es un archivo de texto que describe la estructura visual de una pantalla. Android lee ese archivo y dibuja los elementos en el dispositivo.
Cada archivo XML vive en la carpeta res/layout/ de tu proyecto. El nombre del archivo define cómo Android lo referencia en el código.
Estructura básica de un layout
<?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"
android:padding="16dp">
<TextView
android:id="@+id/tvTitulo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bienvenido a Liverpool"
android:textSize="20sp" />
<Button
android:id="@+id/btnVerProductos"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Ver productos" />
</LinearLayout>
Cada elemento tiene un atributo id que lo identifica de forma única. Ese id es la llave para acceder al elemento desde Kotlin.
Atributos esenciales de cualquier vista
Todos los elementos XML en Android comparten cuatro atributos obligatorios.
| Atributo | Valores comunes | Descripción |
|---|---|---|
layout_width |
match_parent, wrap_content, Xdp |
Ancho del elemento |
layout_height |
match_parent, wrap_content, Xdp |
Alto del elemento |
id |
@+id/nombreElemento |
Identificador único |
android:text |
texto literal o @string/clave |
Texto visible (solo vistas de texto) |
match_parent ocupa todo el espacio disponible del contenedor padre. wrap_content solo ocupa el espacio que necesita el contenido.
¿Qué es View Binding y por qué usarlo?
View Binding es una función de Android que genera automáticamente una clase Kotlin por cada archivo XML de layout. Esa clase tiene una propiedad por cada vista con id.
Antes de View Binding, los desarrolladores usaban findViewById(). Ese método es propenso a errores: si escribes mal el id o buscas el tipo incorrecto, la app se rompe en tiempo de ejecución. View Binding detecta esos errores en tiempo de compilación.
Activar View Binding en el proyecto
Abre el archivo build.gradle del módulo app y agrega lo siguiente dentro del bloque android:
android {
...
buildFeatures {
viewBinding = true
}
}
Sincroniza el proyecto. Android Studio generará una clase de binding por cada layout XML.
Cómo conectar un layout con una Activity
Cuando activas View Binding, Android genera una clase con el nombre del layout en formato PascalCase más el sufijo Binding. Por ejemplo:
activity_main.xml→ActivityMainBindingactivity_catalogo.xml→ActivityCatalogoBinding
Ejemplo 1: Activity básica con TextView y Button
Supón que tienes el layout activity_main.xml con los ids tvTitulo y btnVerProductos.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.tvTitulo.text = "Catálogo Liverpool"
binding.btnVerProductos.setOnClickListener {
binding.tvTitulo.text = "Cargando productos..."
}
}
}
ActivityMainBinding.inflate(layoutInflater) crea el binding y construye la vista. binding.root es el elemento raíz del layout que se pasa a setContentView().
Cada propiedad del objeto binding corresponde directamente a un id del XML. El IDE ofrece autocompletado y detecta errores de tipo.
Ejemplo 2: Formulario de registro con EditText
Imagina una app interna de FEMSA para que sus empleados registren incidencias. El layout activity_registro.xml tiene:
<LinearLayout ...>
<EditText
android:id="@+id/etNombreEmpleado"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Nombre del empleado" />
<EditText
android:id="@+id/etSalario"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Salario mensual"
android:inputType="numberDecimal" />
<Button
android:id="@+id/btnRegistrar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Registrar" />
<TextView
android:id="@+id/tvResultado"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
En Kotlin, el código de la Activity queda así:
class RegistroActivity : AppCompatActivity() {
private lateinit var binding: ActivityRegistroBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityRegistroBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnRegistrar.setOnClickListener {
val nombre = binding.etNombreEmpleado.text.toString()
val salarioTexto = binding.etSalario.text.toString()
if (nombre.isBlank() || salarioTexto.isBlank()) {
binding.tvResultado.text = "Completa todos los campos."
return@setOnClickListener
}
val salario = salarioTexto.toDouble()
binding.tvResultado.text = "Empleado: $nombre — Salario: $$salario"
}
}
}
Nota el uso de return@setOnClickListener para salir del lambda sin salir de la función completa. Es la forma idiomática en Kotlin.
Ejemplo 3: Mostrar precio con formato real
Una app de Mercado Libre México muestra el precio de un artículo. El formato correcto es con signo de pesos y separador de miles.
fun formatearPrecio(precio: Double): String {
val entero = precio.toInt()
return "$${ "%,d".format(entero) }"
}
// En la Activity:
binding.tvPrecio.text = formatearPrecio(18500.0)
// Resultado en pantalla: $18,500
Este patrón evita que el usuario vea textos como $18,500. Siempre formatea los números antes de asignarlos a un TextView.
Contenedores de layout más usados
Elegir el contenedor correcto define cómo se organizan las vistas en pantalla.
| Contenedor | Comportamiento | Cuándo usarlo |
|---|---|---|
LinearLayout |
Organiza vistas en fila o columna | Formularios simples |
ConstraintLayout |
Posiciona vistas con restricciones relativas | Diseños complejos con pocas vistas anidadas |
ScrollView |
Permite desplazamiento vertical | Contenido más largo que la pantalla |
FrameLayout |
Apila vistas una sobre otra | Contenedores de fragmentos |
ConstraintLayout es el recomendado por Google para diseños complejos. Reduce el anidamiento excesivo y mejora el rendimiento.
Errores comunes
1. Olvidar el prefijo @+id/ al definir un id
<!-- Incorrecto -->
android:id="btnRegistrar"
<!-- Correcto -->
android:id="@+id/btnRegistrar"
Sin @+id/, Android no registra el id y View Binding no genera la propiedad. El proyecto no compilará.
2. Llamar setContentView() antes de inflar el binding
// Incorrecto
setContentView(R.layout.activity_main)
binding = ActivityMainBinding.inflate(layoutInflater)
// Correcto
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
Si usas setContentView() con el id del recurso XML y además inflás el binding, trabajas con dos vistas separadas. Los cambios del binding no se verán en pantalla.
3. Leer el texto de un EditText sin llamar .toString()
// Incorrecto — tipo Editable, no String
val nombre = binding.etNombreEmpleado.text
// Correcto
val nombre = binding.etNombreEmpleado.text.toString()
La propiedad text de un EditText devuelve un objeto Editable. Siempre llama .toString() antes de usarlo como cadena de texto.
4. No validar campos vacíos antes de convertir a número
// Riesgoso — lanza excepción si el campo está vacío
val salario = binding.etSalario.text.toString().toDouble()
// Seguro
val salarioTexto = binding.etSalario.text.toString()
val salario = salarioTexto.toDoubleOrNull() ?: 0.0
Usa toDoubleOrNull() en lugar de toDouble(). Si el texto no es un número válido, devuelve null en vez de lanzar una excepción.
Resumen rápido
| Concepto | Descripción |
|---|---|
| Layout XML | Archivo en res/layout/ que define la estructura visual |
id |
Identificador único de cada vista en el XML |
| View Binding | Clase generada automáticamente para acceder a las vistas |
inflate() |
Crea la instancia del binding a partir del layoutInflater |
binding.root |
Vista raíz que se pasa a setContentView() |
.toString() |
Convierte Editable a String en campos de texto |
Con XML defines qué se muestra; con View Binding defines cómo tu código Kotlin interactúa con eso. Dominar ambos es la base de cualquier app Android profesional.