Saltar al contenido

🕒 Mini-Cronómetro en Android con Kotlin y Coroutines (Ejercicio paso a paso)


En esta entrada te propongo un ejercicio práctico y guiado para Android en Kotlin, donde construirás una app sencilla de un cronómetro progresivo usando Coroutines. Es una versión simplificada del clásico ejercicio con múltiples cronos.

Ideal si estás aprendiendo Android y quieres practicar:

  • Interfaces con EditText, TextView y Button
  • Uso de Coroutines para tareas asíncronas
  • Lógica básica de control y validación

🎯 ¿Qué vamos a hacer?

Una app que permita al usuario:

  • Ingresar un número máximo de segundos
  • Iniciar el conteo desde cero hasta ese número
  • Detenerlo o reiniciarlo en cualquier momento

Todo esto se mostrará en una interfaz limpia, sin complicaciones.


🧰 Requisitos

  • Android Studio actualizado
  • Kotlin como lenguaje
  • SDK mínimo: 21 (Android 5.0 Lollipop)
  • Coroutines habilitadas

1. 🛠 Crear el proyecto

  1. Abre Android Studio
  2. Selecciona New Project > Empty Activity
  3. Nombra el proyecto MiniCronometro
  4. Elige Kotlin como lenguaje y minSdk 21

2. ⚙️ Configurar dependencias

Abre tu archivo build.gradle (Module: app) y agrega estas líneas si no están:

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1")
}

Luego sincroniza tu proyecto.


3. 🧩 Crear la interfaz (activity_main.xml)

Copia esto dentro de tu archivo res/layout/activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="24dp"
    android:orientation="vertical"
    android:gravity="center_horizontal">

    <EditText
        android:id="@+id/editMax"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Máximo en segundos"
        android:inputType="number" />

    <TextView
        android:id="@+id/txtCounter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="0"
        android:textSize="64sp"
        android:layout_marginTop="24dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center"
        android:layout_marginTop="24dp">

        <Button
            android:id="@+id/btnStart"
            android:text="Iniciar" />

        <Button
            android:id="@+id/btnStop"
            android:text="Detener"
            android:layout_marginStart="8dp" />

        <Button
            android:id="@+id/btnReset"
            android:text="Reiniciar"
            android:layout_marginStart="8dp" />
    </LinearLayout>
</LinearLayout>

4. 🧠 Lógica en Kotlin (MainActivity.kt)

Reemplaza el contenido de MainActivity.kt con este:

class MainActivity : AppCompatActivity() {

    private lateinit var txtCounter: TextView
    private var job: Job? = null
    private var current = 0
    private var max = 0
    private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val editMax = findViewById<EditText>(R.id.editMax)
        txtCounter = findViewById(R.id.txtCounter)
        val btnStart = findViewById<Button>(R.id.btnStart)
        val btnStop = findViewById<Button>(R.id.btnStop)
        val btnReset = findViewById<Button>(R.id.btnReset)

        btnStart.setOnClickListener {
            max = editMax.text.toString().toIntOrNull() ?: 0
            if (max in 1..9999) {
                startCounter()
            } else {
                editMax.error = "Introduce un número válido (1 a 9999)"
            }
        }

        btnStop.setOnClickListener {
            job?.cancel()
        }

        btnReset.setOnClickListener {
            job?.cancel()
            current = 0
            txtCounter.text = "0"
        }
    }

    private fun startCounter() {
        job?.cancel()
        job = scope.launch {
            while (current < max) {
                delay(1000)
                current++
                txtCounter.text = current.toString()
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        scope.cancel()
    }
}

🧪 Prueba final

  1. Escribe un número (por ejemplo, 10)
  2. Pulsa Iniciar
  3. Verás cómo aumenta el contador cada segundo
  4. Puedes detener o reiniciar en cualquier momento

🧱 Ideas para ampliarlo

  • Agregar un ProgressBar
  • Notificación cuando llegue al máximo
  • Vibrar o reproducir sonido al finalizar
  • Guardar estado usando ViewModel

✅ Conclusión

Este mini-ejercicio te permite entender cómo usar coroutines para manejar tareas temporizadas sin bloquear la interfaz de usuario. Si ya te manejas con este, ¡puedes animarte a hacer un multicronómetro con RecyclerView como el del ejemplo más avanzado!


¿Te gustaría que subiera este código completo a GitHub? ¿Quieres un vídeo paso a paso?
Déjamelo en los comentarios 👇

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Información básica sobre protección de datos Ver más

  • Responsable: Tomas Gonzalez.
  • Finalidad:  Moderar los comentarios.
  • Legitimación:  Por consentimiento del interesado.
  • Destinatarios y encargados de tratamiento:  No se ceden o comunican datos a terceros para prestar este servicio.
  • Derechos: Acceder, rectificar y suprimir los datos.
  • Información Adicional: Puede consultar la información detallada en la Política de Privacidad.

error: Content is protected !!

Descubre más desde Tomás González: Formador y Desarrollador Web

Suscríbete ahora para seguir leyendo y obtener acceso al archivo completo.

Seguir leyendo

Este sitio web utiliza cookies, si necesitas más información puedes visitar nuestra política de privacidad    Ver
Privacidad