안드로이드 CountDownTimer (Kotlin)

Jiyoon Chae·2021년 7월 11일
2

안드로이드

목록 보기
3/23

카운트 다운 기능

개발 개요

: 전세계를 대상으로 하루에 레슨 1개 수강시 챌린지 참여 인증이 되는 챌린지를 구현하기로 했다. 시차가 모두 다르기 때문에 챌린지 인증 시간을 KST로 잡았다. 각 나라 사람들에게 KST가 자정까지(=챌린지 당일 완료를 위한 시간)까지 얼마나 남았는지를 알려주기 위한 카운트다운 기능을 구현해야했다.

개발 내용

: 안드로이드의 내장 함수인 CountDownTimer를 사용해서 구현했다.

https://developer.android.com/reference/kotlin/android/os/CountDownTimer

@RequiresApi(Build.VERSION_CODES.O) 
    private fun updateTime() {
        //set current date  : 오늘 날짜 구하기
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {  //API20이상부터 가능
            // var now = LocalDate.now()  -> 이렇게 하면 더낮은API버전에서 오류가 나서 바꿈
            // var Strnow = currentDate.format(DateTimeFormatter.ofPattern("dd"))
            //오늘 날짜로 초기화 (더 낮은API를 위해 바꿔서 Calendar 클래스 사용)
            val currentDate = Calendar.getInstance()
            val sd = SimpleDateFormat("dd")
            val strnow: String = sd.format(currentDate.time)
						Log.d("DATE - current", currentDate.toString())

            //set event date : 디데이로 설정도 가능하고,내경우엔 다음날 00시로 설정
            val eventDate = Calendar.getInstance()
            eventDate[Calendar.YEAR] = 2021
            eventDate[Calendar.MONTH] = 6     //1~12월을 0~11로 셋팅해야하므로 주의하기!
            eventDate[Calendar.DAY_OF_MONTH] = strnow.toInt() + 1 //다음날 오전00:00 지정
            eventDate[Calendar.HOUR_OF_DAY] = 0
            eventDate[Calendar.MINUTE] = 0
            eventDate[Calendar.SECOND] = 0
            eventDate.timeZone = TimeZone.getTimeZone("Asia/Seoul")  //KST 타임존 셋팅
            Log.d("DATE - event", eventDate.toString())

            //find how many milliseconds until the event  :현재시간과 설정한이벤트 시간 차이 구하기
            val diff = eventDate.timeInMillis - currentDate.timeInMillis //밀리세컨즈로 변환하기
            logTest(eventDate) //로그찍어서 잘나오는지 확인
            logTest(currentDate)
            Log.d("DATE - diff", diff.toString())

            //convert milliseconds to days, hours, minutes and seconds
            val days = diff / (24 * 60 * 60 * 1000)
            //val hours = diff / (1000*60*60)%24
            val hours = diff / (60 * 60 * 1000)
            val minutes = diff / (1000 * 60) % 60
            val seconds = (diff / 1000) % 60
            Log.d(
                "DATE - 숫자로",
                "${days.toString()}, ${hours.toString()}, ${minutes.toString()}, ${seconds.toString()}"
            )
            countDown(diff)  //카운트다운 함수로 1초마다 업데이트
            
        } else {
            Log.d("DATE - error", "카운트 다운 안되는 중------")

        }
    }

    private fun logTest(calendar: Calendar) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            Log.d("test", "test ${calendar.timeZone}")
        }
        Log.d(
            "test",
            "days: ${calendar[Calendar.DAY_OF_MONTH]} hours: ${calendar[Calendar.HOUR_OF_DAY]} minutes: ${calendar[Calendar.MINUTE]} seconds: ${calendar[Calendar.SECOND]}"
        )
    }

    private fun countDown(diff: Long) {
        val days = (diff / (24 * 60 * 60 * 1000)).toString()
/*
        val hours = (diff / (60 * 60 * 1000)).toString()
        val minutes = (diff / (1000 * 60) % 60).toString()
        val seconds = ((diff / 1000) % 60).toString()
*/
					//CountDownTimer 함수 
        countDownTimer = object : CountDownTimer(diff, 1000) {
            
            override fun onTick(p0: Long) {
                val millis: Long = p0
                /* val hms = String.format(
                         "%02d:%02d:%02d:%02d",
                         java.util.concurrent.TimeUnit.HOURS.toDays(java.util.concurrent.TimeUnit.MILLISECONDS.toDays(millis)),
                         (java.util.concurrent.TimeUnit.MILLISECONDS.toHours(millis) - java.util.concurrent.TimeUnit.DAYS.toHours(
                                 java.util.concurrent.TimeUnit.MILLISECONDS.toDays(
                                         millis
                                 )
                         )),
                         (java.util.concurrent.TimeUnit.MILLISECONDS.toMinutes(millis) -
                                 java.util.concurrent.TimeUnit.HOURS.toMinutes(java.util.concurrent.TimeUnit.MILLISECONDS.toHours(millis))),
                         (java.util.concurrent.TimeUnit.MILLISECONDS.toSeconds(millis) - java.util.concurrent.TimeUnit.MINUTES.toSeconds(
                                 java.util.concurrent.TimeUnit.MILLISECONDS.toMinutes(millis)
                         ))
                 ) 레이아웃에 TEXTVIEW 하나에 한줄로 카운트다운 띄울땐 이형식으로 사용*/
								//binding.textViewTimeCountDown.setText(hms);//set text

								//내경우 디자인에서 숫자하나씩 따로 디스플레이 해야했기에 day, hour, min, sec으로 나누는 작업.
                // val days = (java.util.concurrent.TimeUnit.HOURS.toDays(java.util.concurrent.TimeUnit.MILLISECONDS.toDays(millis))).toString()
                val hours =
                    ((java.util.concurrent.TimeUnit.MILLISECONDS.toHours(millis) - java.util.concurrent.TimeUnit.DAYS.toHours(
                        java.util.concurrent.TimeUnit.MILLISECONDS.toDays(millis)
                    ))).toInt()
                val minutes = ((java.util.concurrent.TimeUnit.MILLISECONDS.toMinutes(millis) -
                        java.util.concurrent.TimeUnit.HOURS.toMinutes(
                            java.util.concurrent.TimeUnit.MILLISECONDS.toHours(
                                millis
                            )
                        ))).toInt()
                val seconds =
                    ((java.util.concurrent.TimeUnit.MILLISECONDS.toSeconds(millis) - java.util.concurrent.TimeUnit.MINUTES.toSeconds(
                        java.util.concurrent.TimeUnit.MILLISECONDS.toMinutes(millis)
                    ))).toInt(
            
                setCountdonwTime(days, hours, minutes, seconds) //레이아웃에 적용
                
            }

            override fun onFinish() {
                /*clearing all fields and displaying countdown finished message    */
                // binding.textViewTimeCountDown.setText("Count down completed");
                setCountdonwTime("0", 0, 0, 0) 
                
            }
        }
        countDownTimer.start()
    }//카운트다운 끝

    private fun setCountdonwTime(days: String, hours: Int, minutes: Int, seconds: Int) {

        binding.timerHour1.text = (hours / 10).toString()
        binding.timerHour2.text = (hours % 10).toString()
        binding.timerMinute1.text = (minutes / 10).toString()
        binding.timerMinute2.text = (minutes % 10).toString()
        binding.timerSec1.text = (seconds / 10).toString()
        binding.timerSec2.text = (seconds % 10).toString()

//        registeredUser
        binding.ddayHour1.text = (hours / 10).toString()
        binding.ddayHour2.text = (hours % 10).toString()
        binding.ddayMin1.text = (minutes / 10).toString()
        binding.ddayMin2.text = (minutes % 10).toString()
    }

레이아웃

개발 중 이슈

  1. API 20이하 버전까지도 가능하게 하기

    → 해결 : IDE에서 제안하는 방법을 쓰고, 예외처리로 처리함. 그래도 안되는 부분은 같은 기능이지만 낮은 버전에서 사용가능한 클래스나 인터페이스를 찾아서 바꿈.

  2. 카운트 다운 함수가 새로운 액티비티가 위로 쌓였을 때 밑에서 계속 동작하고있어서 중단시켜야했음.

    → 해결 :

    현재 보이는 액티비티가 실행될 때만 카운트다운 함수가 동작하고, 새로운 액티비티 실행시 정지 하기. → 새로운 액티비티에서 다시 뒤로가기로 돌아왔을 때 카운트다운이 실행되게 하기.

    액티비티의 실행 주기를 잘 이해하면 해결 할 수 있는 문제였다.

    • onResume()에서 실행하고, onPause()에서 정지시키기.
    //---24시간 카운트다운--------------------
        @RequiresApi(Build.VERSION_CODES.O)
        override fun onResume() {
            super.onResume()
            updateTime()
        }
    
        override fun onPause() {
            super.onPause()
            countDownTimer.cancel()
        }

    onCreate() : 액티비티가 처음 생성될 때 호출되는 함수 →

    onPause() 함수 : 해당 액티비티에서 다른 액티비티로 전환되기 직전에 호출되는 함수. 보통 해당 함수에서는 아직 저장되지 않은 데이터를 유지하기 위해 데이터를 저장하거나 시스템 자원을 소모하는 작업을 진행하는 경우 중단할 때 사용됩니다. 예를 들어 동영상을 재생하고 있었다가 다른 액티비티를 전환되는 경우 해당 함수에서 동영상을 멈추도록 하는 구현이 들어갈 수 있습니다. 해당 함수가 끝나기 전까지 액티비티 전환이 이루어지지 않기 때문에 해당 함수는 빠르게 작업이 이루어져야 합니다.

    액티비티가 다시 표시되기 시작하면 (전환된 액티비티에서 이전 버튼 클릭) 다시 onResume() 함수가 호출되고 전환된 액티비티에 의해 액티비티가 가려지게 되면 onStop() 함수가 호출됩니다.

    *참고한 블로그 :https://lktprogrammer.tistory.com/164

profile
바닐라라떼 좋아☕

0개의 댓글