라떼는말이야

[Android] Countdown Timer 예제 본문

안드로이드

[Android] Countdown Timer 예제

MangBaam 2021. 8. 5. 02:26
반응형

UI 디자인 <activity_main.xml>

Countdown Timer UI

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/remainMinutesTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="00"
        android:textSize="40sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/remainSecondsTextView"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="HardcodedText" />


    <TextView
        android:id="@+id/remainSecondsTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="00"
        android:textSize="40sp"
        app:layout_constraintBottom_toBottomOf="@+id/remainMinutesTextView"
        app:layout_constraintLeft_toRightOf="@+id/remainMinutesTextView"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="@+id/remainMinutesTextView"
        tools:ignore="HardcodedText" />

    <SeekBar
        android:id="@+id/seekBar"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:max="60"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/remainMinutesTextView" />

</androidx.constraintlayout.widget.ConstraintLayout>

XML에서 모르는 부분은 댓글로 물어봐주세요. 따로 설명하지는 않겠습니다.


 

Android - CountDownTimer

안드로이드에서 카운트다운을 위한 메소드를 제공해준다.

public abstract class CountDownTimer

사용법은 다음과 같다.

 object : CountDownTimer(30000, 1000) {

     override fun onTick(millisUntilFinished: Long) {
         mTextField.setText("seconds remaining: " + millisUntilFinished / 1000)
     }

     override fun onFinish() {
         mTextField.setText("done!")
     }
 }.start()

CountDownTimer의 인자로는 millisInFuture: long과 countDownInterval: long이 입력된다.

millisInFuture은 카운트다운을 할 전체 시간을 의미하는데 이름에서도 알 수 있다시피 밀리세컨드(ms) 단위이다.

countDownInterval은 카운트다운이 진행되는 동안 얼만큼의 시간 간격으로 반복적으로 특정 작업을 시행할지를 구현할 수 있다.

 

만약 위 예제처럼 countDownInterval을 1000으로 줬다면 매 초마다 onTick() 메소드를 실행하는 것이다.

onTick 메소드의 매개변수인 millisUntilFinished는 CountDown이 종료될 때까지 남은 시간(ms)을 의미한다.

 

CountDown이 종료되면 onFinish() 메소드가 실행된다.

 

CountDownTimer 내부에서 onTick과 onFinish를 구현하고 마지막에 .start()를 해주면 동작하게 된다.

 

만약 countdown을 중지하고 싶다면 cancel() 메소드를 사용하면 된다.


 

Android - OnSeekBarChangeListener

위에서 만든 UI에서 하단에 ProgressBar와 초록색 핸들이 있다. (초록색 핸들의 정식 명칭은 Thumb이다)

이를 안드로이드에서 SeekBar라고 한다.

Thumb를 잡고 움직이면 값을 설정할 수 있는 컴포넌트이다. 이때 발생하는 이벤트가 OnSeekBarChangeListener이다.

 

우선 SeekBar에 이벤트를 달아줘야한다. 그리고 그 내부에서 OnSeekBarChangeListener를 구현한다.

seekBar.setOnSeekBarChangeListener (
            object: SeekBar.OnSeekBarChangeListener {
                override fun onProgressChanged(
                    seekBar: SeekBar?,
                    progress: Int,
                    fromUser: Boolean
                ) {
                    
                }

                override fun onStartTrackingTouch(seekBar: SeekBar?) {
                    
                }

                override fun onStopTrackingTouch(seekBar: SeekBar?) {
                    
                }
            }
        )

OnSeekBarChangeListener 내부에는 구현해야하는 3개의 추상 메소드가 있다.

  • onProgressChanged()
  • onStartTrackingTouch()
  • onStopTrackingTouch()

OnSeekBarChangeListener를 열고 내부에 아직 아무것도 작성하지 않은 상태에서 오류 메시지를 확인하면 아직 구현되지 않은 추상 멤버들이 존재한다고 뜬다.

Ctrl + o

그 상태에서 [Ctrl + o] 를 누르면 Override 할 수 있는 목록이 뜬다.

Ctrl을 누른 상태에서 여러 개를 선택할 수 있기 때문에 밑의 3개 메소드를 선택하면 간단히 만들 수 있다.


 

전체 소스코드 <MainActivity.kt>

import android.annotation.SuppressLint
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.CountDownTimer
import android.widget.SeekBar
import android.widget.TextView

class MainActivity : AppCompatActivity() {

    private val remainMinutesTextView: TextView by lazy {
        findViewById(R.id.remainMinutesTextView)
    }

    private val remainSecondsTextView: TextView by lazy {
        findViewById(R.id.remainSecondsTextView)
    }

    private val seekBar: SeekBar by lazy {
        findViewById(R.id.seekBar)
    }

    private var currentCountDownTimer: CountDownTimer? = null

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

        bindViews()
    }

    private fun bindViews() {
        seekBar.setOnSeekBarChangeListener (
            object: SeekBar.OnSeekBarChangeListener {
                override fun onProgressChanged(
                    seekBar: SeekBar?,
                    progress: Int,
                    fromUser: Boolean
                ) {
                    if(fromUser) {
                        updateRemainTime(progress * 60 * 1000L)
                    }
                }

                override fun onStartTrackingTouch(seekBar: SeekBar?) {
                    currentCountDownTimer?.cancel()
                    currentCountDownTimer = null
                }

                override fun onStopTrackingTouch(seekBar: SeekBar?) {
                    seekBar ?: return

                    currentCountDownTimer = createCountDownTimer(seekBar.progress * 60 * 1000L)
                    currentCountDownTimer?.start()
                }
            }
        )
    }

    private fun createCountDownTimer(initialMillis: Long) =
        object: CountDownTimer(initialMillis, 1000) {
            override fun onTick(millisUntilFinished: Long) {
                updateRemainTime(millisUntilFinished)
                updateSeekBar(millisUntilFinished)
            }

            override fun onFinish() {
                updateRemainTime(0)
                updateSeekBar(0)
            }
        }

    @SuppressLint("SetTextI18n")
    private fun updateRemainTime(remainMillis: Long) {
        val remainSeconds = remainMillis / 1000

        remainMinutesTextView.text = "%02d".format(remainSeconds / 60)
        remainSecondsTextView.text = "%02d".format(remainSeconds % 60)
    }

    private fun updateSeekBar(remainMillis: Long) {
        seekBar.progress = (remainMillis / 1000 / 60).toInt()
    }
}
반응형
Comments