Android – 스톱워치 레퍼런스 with. 코틀린

StopWatch 에 들어간 클래스/위젯
class, component on the StopWatch

  • Timer
  • TextView. Button
  • ScrollView

activity_main.xml

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

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="0"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" android:id="@+id/secTextView" android:textSize="100sp"
            android:textAppearance="@style/TextAppearance.AppCompat.Large"
            app:layout_constraintVertical_bias="0.100000024"/>
    <TextView
            android:text="00"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/milliTextView"
            android:textAppearance="@style/TextAppearance.AppCompat.Large" app:fontFamily="sans-serif-black"
            app:layout_constraintStart_toEndOf="@+id/secTextView" android:layout_marginStart="8dp"
            android:layout_marginLeft="8dp"
            app:layout_constraintBaseline_toBaselineOf="@+id/secTextView"/>
    <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:clickable="true" app:srcCompat="@drawable/ic_play_arrow_black_24dp"
            android:id="@+id/fab" app:backgroundTint="@color/colorPrimary"
            app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="16dp"
            android:tint="@android:color/white" tools:srcCompat="@drawable/ic_play_arrow_black_24dp"
            app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/>
    <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:clickable="true" app:srcCompat="@drawable/ic_refresh_black_24dp"
            android:id="@+id/resetFab" app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="16dp"
            android:layout_marginLeft="16dp" android:layout_marginStart="16dp" app:backgroundTint="#E91E63"
            android:tint="@android:color/white"/>
    <Button
            android:text="랩 타임 "
            android:layout_width="93dp"
            android:layout_height="55dp"
            android:id="@+id/lapButton"
            app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="16dp"
            app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp" android:textAppearance="@style/TextAppearance.AppCompat.Medium"/>
    <ScrollView
            android:layout_width="wrap_content"
            android:layout_height="0dp" android:layout_marginTop="8dp"
            app:layout_constraintTop_toBottomOf="@+id/secTextView" android:layout_marginBottom="8dp"
            app:layout_constraintBottom_toTopOf="@+id/fab" app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:id="@+id/scrollView2">
        <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
                      android:orientation="vertical" android:id="@+id/lapLayout"/>
    </ScrollView>
    <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:clickable="true" app:srcCompat="@drawable/ic_clear_black_24dp"
            android:id="@+id/clear"
            android:tint="@android:color/white" app:backgroundTint="@color/design_default_color_primary"
            android:visibility="invisible" android:layout_marginBottom="8dp"
            app:layout_constraintBottom_toTopOf="@+id/resetFab" app:layout_constraintStart_toStartOf="parent"
            android:layout_marginLeft="16dp" android:layout_marginStart="16dp"/>

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

package com.example.stop_watch

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import android.widget.Toast
import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.activity_main.*
import java.util.*
import kotlin.concurrent.timer

class MainActivity : AppCompatActivity() {
    private var time=0 // 0.01초마다 1씩 오르는 변수
    private var isRunning=false // 스톱워치 실행이 되면 true가 됨
    private var timerTask: Timer?=null // Timer객체를 가리키는 참조변수
    private var lap=1 // 저장이 몇 개 됐는지 알리는 변수 
    private var lastTimeBackPressed:Long=-1500 // 이전에 버튼을 눌렀을 때 시간

    private fun start(){
        clear.isVisible=false // 외관상 좋게 보이기 위해 x버튼 감추기
        fab.setImageResource(R.drawable.ic_pause_black_24dp)

        // timer고차함수에 period인자와 블록{} 내 함수를 보냄 -> Timer객체를 반환
        // timer에서는 전달받은 함수를 0.01초에 한 번씩 호출
        timerTask=timer(period=10){
            time++
            val sec=time/100 // 1초가 지나면 time은 100이되는데 이 때 sec은 1이 됨
            val milli=time%100 // milli는 1~99까지만 출력되어야 하니 100으로 나눈 나머지임

            // runonuithread메소드 호출 (ui관련 객체를 전달) <sam변환>
            // 힘든 작업을 할 때는 워커 스레드에서 작용하는데 timer함수가 그렇다.
            // 워커 스레드 작업 중에는 ui변경이 불가능하다. 하지만 runonuithread에 전달되는 클래스는 워커 스레드에서 벗어난다.
            runOnUiThread{
                secTextView.text="$sec"
                milliTextView.text="$milli"
            }
        }
    }

    private fun pause(){
        fab.setImageResource(R.drawable.ic_play_arrow_black_24dp)
        clear.isVisible=true
        // Timer객체의 cancel메소드로 타이머 중지 가능
        timerTask?.cancel()
    }

    private fun reset(num:Int){
        timerTask?.cancel() // 멈춤
        clear.isVisible=true // x 버튼 보이게 함

        // reset됐는데 isRunning이 true인 상태면 false로 만듬
        if(isRunning)
            isRunning=false

        // time초기화 및 시작 버튼 바꿈
        time=0
        fab.setImageResource(R.drawable.ic_play_arrow_black_24dp)

        if(num==1){ // clear버튼 눌렀을 때는 리니어 레이아웃 모든 위젯 제거/ 텍스트,lap초기화
            lapLayout.removeAllViews()
            secTextView.text="0"
            milliTextView.text="00"
            lap=1
            clear.isVisible=false // x 버튼 감춤
        }
    }

    private fun recordLapTime(){
        // start메소드에서 계속 오르는 time변수를 가져옴
        val lapTime=this.time

        // 위젯을 동적 생성
        val textView= TextView(this)
        val string="$lap LAP:${lapTime/100}.${lapTime%100}"
        textView.text=string

        // 스크롤뷰 맨 위(인덱스:0)에 위젯을 추가
        lapLayout.addView(textView,0)
        lap++
    }

    // 이전 버튼 두 번 눌러서 종료하기
    override fun onBackPressed() {
        // (현재 버튼 누른 시간-이전에 버튼 누른 시간) <=1.5초일 때 동작
        if(System.currentTimeMillis()-lastTimeBackPressed<=1500)
            finish()
        lastTimeBackPressed=System.currentTimeMillis()
        Toast.makeText(this,"이전 버튼을 한 번 더 누르면 종료됩니다",Toast.LENGTH_SHORT).show()
    }

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

        fab.setOnClickListener{
            // fab이 클릭되면 isRunning을 반전
            when(isRunning){
                false->start() // false: 현재 실행되지 않는 상태이니 start가능, start시 변수 반전
                else->pause() // true: 현재 실행되는 상태이니 pause가능, pause시 변수 반전
            }
            isRunning=!isRunning
        }

        lapButton.setOnClickListener{
            recordLapTime()
        }

        resetFab.setOnClickListener{
            reset(0)
        }

        clear.setOnClickListener{
            reset(1)
        }
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *