Construcción de la clásica “calculadora” en Android Studio y Kotlin

En este laboratorio vamos a construir la clásica calculadora básica usando Android Studio y Kotlin
1. AndroidManifest.xml
Este archivo es como el “acta de nacimiento” de tu aplicación. Android lo usa para saber qué contiene tu app.
Código relevante
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.Calculadora">
<activity android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
Conceptos importantes:
application
→ Contenedor principal de tu app.android:icon
→ Icono en el celular.android:label
→ Nombre que aparece bajo el icono.android:theme
→ Estilo visual global.activity
→ Representa una pantalla. Aquí solo tenemos MainActivity.intent-filter
→ Define cómo se abre la app:MAIN
→ Actividad principal.LAUNCHER
→ Aparece en el menú del dispositivo.
2. vista_principal.xml
Este archivo define la interfaz gráfica. Android usa XML para estructurar cómo se verá la pantalla.
Contenedor
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
ConstraintLayout
→ Permite organizar vistas con restricciones.match_parent
→ El elemento ocupa todo el espacio disponible.
TextView de entrada y resultado
<TextView
android:id="@+id/operandoCalculadora"
android:textSize="48sp"/>
<TextView
android:id="@+id/resultadoCalculadora"
android:textSize="60sp"/>
TextView
→ Texto que se muestra en pantalla.id
→ Identificador único para enlazarlo con Kotlin (findViewById
).sp
→ Unidad recomendada para texto (escala con accesibilidad).
Teclado con GridLayout
<GridLayout
android:columnCount="4">
GridLayout
→ Organiza los botones en una cuadrícula 4×4.
Ejemplo de botón:
<com.google.android.material.button.MaterialButton
android:text="7"
android:onClick="seleccionarNumero"
app:backgroundTint="#FF3D00"
app:cornerRadius="40dp"/>
Propiedades clave:
text
→ El número u operador que muestra el botón.onClick
→ Llama a un método enMainActivity.kt
.backgroundTint
→ Color de fondo.cornerRadius
→ Bordes redondeados.
El atributo onClick="seleccionarNumero"
conecta el botón con la función Kotlin seleccionarNumero()
.
3. MainActivity.kt
Este es el cerebro de la app. Aquí controlamos cómo responden los botones.
Encabezado
class MainActivity : AppCompatActivity() {
class
→ Declara una clase.MainActivity
→ Nombre de nuestra pantalla principal.:
→ Herencia en Kotlin (similar aextends
en Java).AppCompatActivity()
→ Clase base que da a nuestra app funciones de actividad.
Variables globales
private var operacionActual = ""
private var primerNumero: Double = Double.NaN
private
→ Solo accesible dentro de esta clase.var
→ Variable mutable (puede cambiar).Double.NaN
→ “Not a Number”, indica que aún no hay número válido.
private lateinit var operandoCalculadora: TextView
private lateinit var resultadoCalculadora: TextView
private lateinit var formatoDecimal: DecimalFormat
lateinit
→ Inicialización diferida (se asigna después enonCreate
).TextView
→ Referencia a la vista XML.DecimalFormat
→ Sirve para dar formato a los resultados.
Método onCreate
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.vista_principal)
override
→ Sobrescribe un método de la clase padre.onCreate
→ Se ejecuta al iniciar la actividad.setContentView
→ Vincula el XML con el código Kotlin.
Guardar estado
override fun onSaveInstanceState(outState: Bundle) {
outState.putDouble("primerNumero", primerNumero)
}
- Guarda datos si se gira la pantalla.
Bundle
es como un diccionario (clave–valor).
Función auxiliar
private fun tienePrimerNumero() = !primerNumero.isNaN()
- fun → Declara una función.
- = → Función de una sola expresión.
- Devuelve true si ya hay un número válido.
Función cambiarOperador
fun cambiarOperador(v: View) {
val boton = v as Button
calcular()
operacionActual = when (boton.text.toString().trim()) {
"X" -> "*"
"÷" -> "/"
else -> boton.text.toString().trim()
}
}
val
→ Variable inmutable (no cambia después de asignarse).as
→ Conversión de tipo (View
aButton
).when
→ Condicional múltiple, similar aswitch
.
Función calcular
fun calcular() {
if (operandoCalculadora.text.isEmpty()) return
val valor = operandoCalculadora.text.toString().toDoubleOrNull() ?: return
if
→ Condicional básico.toDoubleOrNull()
→ Convierte texto a número o devuelvenull
.?:
(operador Elvis) → Si esnull
, retorna inmediatamente.
if (operacionActual == "/" && valor == 0.0) {
resultadoCalculadora.text = "Error"
return
}
- Manejo de división por cero.
Función seleccionarNumero
fun seleccionarNumero(v: View) {
val texto = (v as Button).text.toString()
operandoCalculadora.text = operandoCalculadora.text.toString() + texto
}
- Concatenación de texto en Kotlin con
+
.
Función igual
fun igual(v: View) {
calcular()
if (!primerNumero.isNaN()) {
resultadoCalculadora.text = formatoDecimal.format(primerNumero)
}
operacionActual = ""
}
!
→ Negación lógica (NOT).- Muestra el resultado en formato decimal.
Función borrar
fun borrar(v: View) {
val etiqueta = (v as Button).text.toString().trim()
if (etiqueta == "C") {
val s = operandoCalculadora.text.toString()
if (s.isNotEmpty()) {
operandoCalculadora.text = s.dropLast(1)
} else {
primerNumero = Double.NaN
operacionActual = ""
resultadoCalculadora.text = ""
}
} else if (etiqueta == "CA") {
primerNumero = Double.NaN
operacionActual = ""
operandoCalculadora.text = ""
resultadoCalculadora.text = ""
}
}
isNotEmpty()
→ Comprueba si el texto no está vacío.dropLast(1)
→ Quita el último carácter.
Conceptos de Kotlin aplicados en este proyecto
Variables:
val
→ inmutable.var
→ mutable.lateinit
→ inicialización diferida.
Funciones:
fun nombre()
→ define una función.override
→ sobrescribir funciones de clases padres.- Funciones de una sola línea con
=
.
Condicionales:
if/else
.when
(switch mejorado).
Operadores útiles:
?.
→ llamada segura (evitaNullPointerException
).?:
→ operador Elvis (valor alternativo).!
→ negación lógica.
Clases y objetos:
AppCompatActivity
es una clase base.MainActivity
hereda de ella.- Uso de
as
para casting de tipos (View
→Button
).
Strings y números:
.toString()
convierte a texto..toDoubleOrNull()
convierte texto a número.DecimalFormat
→ formatea números.