또는

스톱워치(초시계)

 

기능 소개

타이머 시작, 일시정지, 초기화, 랩타임 표시등의 기능을 갖는 초시계를 제작해 봅니다.

 

핵심이 되는 주제

timer

백그라운드 스레드

runOnUiThread

ScrollView

FloatingActionButton

 

 

실습

1) 빈 액티비티를 생성 (앱 이름 : StopWatch) 

     벡터 이미지를 사용할 것이므로 vectorDrawables.useSupportLibrary = true 설정

      (모듈수준 bundle.gradle의 defaultConfig{} 에 작성하고, 'New Sync' 클릭해 주는 거 있지 않으셨죠?^^ )

 

2) 레이아웃 작업

 

  ① 두 개의 TextView (초, 백분의 1초 시간 표시용)를 배치합니다.

       (id : 각각 secTextView, milliTextView)

 

      참고) 뷰의 컨텍스트 메뉴에서 기준라인을 보이게 함으로써 두 뷰의 글자 하단을 정렬할 수 있어요.

       

[ 텍스트 기준선 정렬 ]

 

② 세 개의 벡터이미지 준비해 주세요.

     play arrow(시작), pause(일시정지), refresh(초기화)

     

[ 세 개의 벡터 이미지 ]

 

③ 2개의 FloatingActionButton(FAB)을 배치해야 하는데요.

   - FAB는 벡터 이미지로 깔끔한 버튼을 만들기에 적합한 이미지합성 버튼입니다.

   - 구글 머티리얼 디자인에 자주 사용됩니다.
   - 컴포넌트 팔레트 창에서 처음 이 컴포넌트를 추가하려고 하면 네트워크 상에서 다운로드 받도록 되어 있습니다.

      (다운로드 받으세요)

   - 다운로드가 끝났으면 FAB를 드래그앤드롭으로 가져다 놓고,

     벡터 이미지(시작, 초기화 이미지)를 선택하고 id와 색상을 적당히 설정하세요.

     총 2개의 버튼을 만들면 됩니다.

      참고) 시작 버튼 id: playFab, 초기화 버튼 id: resetFab

             

[ 두 개의 FAB ]

  ④ 1개의 Button (랩 타임용)을 배치하세요 - id: labButton

  ⑤ 중앙에 ScrollView (랩 타임 표시용)를 배치해 주세요.
     수직으로 차곡차곡 쌓이는 레이아웃을 LinearLayout이라고 합니다.
     (컴포넌트 트리창을 보면 ScrollView 하위에 LinearLayout이 들어있을 거예요. 
      ScrollView 뷰는 자식 뷰룰 하나만 갖는 특수한 뷰이고,
      LinearLayout 뷰는 여러 개의 자식 뷰를 갖습니다)
      LinearLayout 안에 동적으로 타임랩 값들을 새로운 형태로 쌓이도록 만들것입니다.

     컴포넌트 트리 창에서 LinearLayout 뷰를 선택하고 id를 입력해 주세요. ( 예: lapLayout )

[ 최종 레이아웃 ]

  

 

 

3) 코딩

 

timer (타이머)

    안드로이드의 두 가지 스레드 모드가 있습니다.

       ① 메인스레드 (일반적인 UI 가능)

       워커스레드 (작업 시간이 오래 걸리고 화면에 표시되지 않음. 당연히 UI 조작을 불가)

 

  타이머 코딩 형식

    timer (period = 1000) {        // 1000 = 1

        // 워커스레드 (UI조작 불가)

        runOnUiThread {

            // 메인스레드 (UI조작 가능)

        }

    }

 

 

- MainActivity.kt

 

package com.tistory.stopwatch

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

class MainActivity : AppCompatActivity() {


    private var time = 0
    private var timerTask: Timer? = null      // null을 허용
    private var isRunning = false
    private var lap = 1

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

        playFab.setOnClickListener {
            isRunning = !isRunning

            if (isRunning) { start() } else { pause() }
        }

        labButton.setOnClickListener {
            recordLapTime()
        }

        resetFab.setOnClickListener {
            reset()
        }
    }

    private fun start() {
        playFab.setImageResource(R.drawable.ic_pause_black_24dp)   // 일시정지 이미지

        timerTask = timer(period=10) {       // 타이머 인터벌 10 ms
            time++

            val sec = time / 100
            val milli = time % 100

            runOnUiThread {                   // UI 조작이 가능한 블럭
                secTextView.text = "$sec"
                milliTextView.text = "$milli"
            }
        }
    }

    private fun pause() {
        playFab.setImageResource(R.drawable.ic_play_arrow_black_24dp)   // 시작 이미지

        timerTask?.cancel()                  // 실행중인 타이머 취소
    }

    private fun recordLapTime() {
        if (!isRunning) return         // 타이머가 실행 중이 아니라면 리턴
        val lapTime = this.time
        val textView = TextView(this)      // 동적으로 TextView 생성
        textView.text = "$lap LAB : ${lapTime / 100}. ${lapTime % 100}"

        lapLayout.addView(textView, 0)     // 0 : 맨 위쪽에 추가
        lap++
    }

    private fun reset() {
        timerTask?.cancel()       // 실행중인 타이머 취소

        // 모든 변수 초기화
        time = 0
        isRunning = false
        playFab.setImageResource(R.drawable.ic_play_arrow_black_24dp)
        secTextView.text = "0"
        milliTextView.text = "0"

        // 모든 랩타임 기록 삭제
        lapLayout.removeAllViews()
        lap = 1
    }

}

 

간단하지만 실행해보면 훌륭하게 동작하는 타이머가 완성됐습니다!

이번 프로그램은 비교적 간단했네요. 

 

다음에는 자신만의 웹 브라우저를 제작해 봅시다~

 

 

<<< 이전 글 보기      다음 글 보기 >>>

 

 

 

+ Recent posts