๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Study/Android

[์•ˆ๋“œ๋กœ์ด๋“œ ์ฝ”ํ‹€๋ฆฐ] 9. ์‹ค์ „ํ”„๋กœ์ ํŠธ(2) - ์Šคํ†ฑ์›Œ์น˜

์‹ค์ „ ํ”„๋กœ์ ํŠธ

์‹ค์Šต - ์Šคํ†ฑ ์›Œ์น˜

์ฃผ์š”๊ตฌ์„ฑ

- ๋น ๋ฅด๊ฒŒ ๊ณ„์‚ฐํ•˜๋ฉด์„œ UI๋ฅผ ๊ฐฑ์‹ 

- ๊ฐ๊ฐtimer์™€ runOnUiThread ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ๊ตฌํ˜„

- ๋žฉํƒ€์ž„์„ ๋ˆ„์ ํ•˜์—ฌ ํ‘œ์‹œํ•  ScrollView์— ๋™์ ์œผ๋กœ TextView๋ฅผ ์ถ”๊ฐ€

 

1. ํ™”๋ฉด๊ตฌ์„ฑ

- ์‹œ๊ฐ„์„ ํ‘œ์‹œํ•˜๋Š” TextView 2๊ฐœ

- ํƒ€์ด๋จธ๋ฅผ ์‹œ์ž‘ ๋ฐ ์ผ์‹œ์ •์ง€, ์ดˆ๊ธฐํ™”ํ•˜๋Š” FloatingActionButton (๋ฒกํ„ฐ ์•„์ด์ฝ˜ ์ด๋ฏธ์ง€ 3๊ฐœ ์ถ”๊ฐ€)

    - tint๋กœ ์ƒ‰์ƒ ๋ฐ”๊พธ๊ธฐ → xml ์ฝ”๋“œ์—์„œ app:tint="@color/white"์ถ”๊ฐ€

    -   FloatingActionButton ์ถ”๊ฐ€์‹œ No speakable text present๋ผ๋Š” ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ activity_main.xml์—์„œ FloatingActionButtom ์†์„ฑ์— tools:ignore="SpeakableTextPresentCheck"๋ฅผ ์ถ”๊ฐ€

- ‘๋žฉ ํƒ€์ž„’ ๋ฒ„ํŠผ

- ๋žฉ ํƒ€์ž„์„ ๊ธฐ๋กํ•˜๊ณ  ํ‘œ์‹œํ•  ScrollView

 

2. ํƒ€์ด๋จธ ๊ตฌํ˜„ํ•˜๊ธฐ

timer ์‚ฌ์šฉ ๋ฐฉ๋ฒ•

- ์ฝ”ํ‹€๋ฆฐ์—์„œ ์ผ์ •ํ•œ ์‹œ๊ฐ„์„ ์ฃผ๊ธฐ๋กœ ๋ฐ˜๋ณตํ•˜๋Š” ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•  ๋•Œ๋Š” timer ๊ธฐ๋Šฅ ์‚ฌ์šฉ

// 1000ms(1์ดˆ) ๊ฐ„๊ฒฉ์œผ๋กœ ์–ด๋–ค ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ฝ”๋“œ
timer(period = 1000 {
	// ์ˆ˜ํ–‰ํ•  ๋™์ž‘
}

- ์›Œ์ปค ์Šค๋ ˆ๋“œ์—์„œ๋Š” UI๋ฅผ ์กฐ์ž‘ํ•  ์ˆ˜ ์—†์–ด runOnUiThread( ) ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•จ

// timer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ 1์ดˆ๋งˆ๋‹ค UI๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์ฝ”๋“œ
tiemr(period = 1000) {
    // ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…
    runOnUiThread{
        // UI ์กฐ์ž‘
    }
}

 

ํƒ€์ด๋จธ ์‹œ์ž‘ ๊ตฌํ˜„

- ํƒ€์ด๋จธ๋ฅผ ์‹œ์ž‘ํ•  ๋•Œ ํ˜ธ์ถœํ•  start ( ) ๋ฉ”์„œ๋“œ๋ฅผ ์ž‘์„ฑ

class MainActivity : AppCompatActivity() {

    private var time = 0;
    private var timerTask: Timer? = null

    lateinit var fab: FloatingActionButton
    lateinit var secTextView: TextView
    lateinit var milliTextView: TextView

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

        fab = findViewById<FloatingActionButton>(R.id.fab)
        secTextView = findViewById<TextView>(R.id.secTextView)
        milliTextView = findViewById<TextView>(R.id.milliTextView)

    }

    private fun start(){
        fab.setImageResource(R.drawable.baseline_pause_24)

        timerTask = timer(period=10){
            time++
            val sec = time / 100
            val milli = time % 100
            runOnUiThread {
                secTextView.text = "$sec"
                milliTextView.text = "$milli"
            }
        }
    }
}

 

ํƒ€์ด๋จธ ์ผ์‹œ์ •์ง€ ๊ตฌํ˜„

- ์ผ์‹œ์ •์ง€ ๋ฉ”์„œ๋“œ์ธ pause( ) ๋ฉ”์„œ๋“œ๋ฅผ ์ž‘์„ฑ

    private fun pause(){
        fab.setImageResource(R.drawable.baseline_play_arrow_24)  // fab ๋ˆ„๋ฅด๋ฉด fab์˜ ์ด๋ฏธ์ง€(์‹œ์ž‘) ๋ณ€๊ฒฝ
        timerTask?.cancel()      // ํ•˜๋Š” ์ผ ๋ฉˆ์ถ”๊ธฐ
    }

 

๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ

- MainActivity.kt

- isRunning : play/pause ๊ตฌ๋ณ„์„ ์œ„ํ•œ ํ”Œ๋ž˜๊ทธ ๋ณ€์ˆ˜

    private var time = 0;   // ์‹œ๊ฐ„์„ ๊ณ„์‚ฐํ•  ๋ณ€์ˆ˜
    private var timerTask: Timer? = null
    private var isRunning = false       // ์ž‘๋™ ์ค‘: ์ฒ˜์Œ์€ false(์ž‘๋™X)

    lateinit var fab: FloatingActionButton
    lateinit var secTextView: TextView
    lateinit var milliTextView: TextView

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

        fab = findViewById<FloatingActionButton>(R.id.fab)
        secTextView = findViewById<TextView>(R.id.secTextView)
        milliTextView = findViewById<TextView>(R.id.milliTextView)

        fab.setOnClickListener {    // fab(์ผ์‹œ์ •์ง€/์‹œ์ž‘) ๋ฒ„ํŠผ
            isRunning = !isRunning         // ๋ฌด์กฐ๊ฑด ์ผ์‹œ์ •์ง€ ์•„๋‹ˆ๋ฉด ์‹œ์ž‘ ์„œ๋กœ ๋ฐ˜๋Œ€๋กœ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก

            if(isRunning){  // ์ž‘๋™O
                start()     // timer ์‹œ์ž‘
            }
            else{           // ์ž‘๋™X
                pause()     // timer ๋ฉˆ์ถค
            }
        }
    }

 

3. ๋žฉ ํƒ€์ž„ ๊ธฐ๋กํ•˜๊ธฐ

๋™์ ์œผ๋กœ LinearLayout์— ๋ทฐ ์ถ”๊ฐ€ํ•˜๊ธฐ

- LinearLayout์— ๋™์ ์œผ๋กœ ๋ทฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ addView( ) ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉ

val textView = TextView(this)
textView.text = "๊ธ€์ž"
lapLayout.addView(textView)

- ํ•ญ์ƒ ๋งจ ์œ„(0)์— ํ…์ŠคํŠธ ๋ทฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ์ฝ”๋“œ (์ด ์ฝ”๋“œ ์กฐ๊ฐ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋žฉ ํƒ€์ž„์„ ๊ธฐ๋กํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Œ)

lapLayout.addView(textView, 0)

 

๋žฉ ํƒ€์ž„์„ ํ‘œ์‹œ

- ๋žฉ ํƒ€์ž„ ๊ธฐ๋ก ๋ฐ ํ‘œ์‹œํ•˜๋Š” recordLapTime( ) ๋ฉ”์„œ๋“œ ์ž‘์„ฑ

class MainActivity : AppCompatActivity() {

    private var time = 0;   // ์‹œ๊ฐ„์„ ๊ณ„์‚ฐํ•  ๋ณ€์ˆ˜
    private var timerTask: Timer? = null
    private var isRunning = false       // ์ž‘๋™ ์ค‘: ์ฒ˜์Œ์€ false(์ž‘๋™X)
    private var lap = 1     // ๋ช‡๋ฒˆ์งธ ๋žฉ์ธ์ง€ ํ‘œ์‹œํ•˜๋Š” ๋ณ€์ˆ˜(๋ฌด์กฐ๊ฑด 1๋ถ€ํ„ฐ ์‹œ์ž‘)

    lateinit var fab: FloatingActionButton
    lateinit var secTextView: TextView
    lateinit var milliTextView: TextView
    lateinit var labLayout: LinearLayout
    lateinit var labButton: Button

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

        fab = findViewById<FloatingActionButton>(R.id.fab)
        secTextView = findViewById<TextView>(R.id.secTextView)
        milliTextView = findViewById<TextView>(R.id.milliTextView)
        labLayout = findViewById<LinearLayout>(R.id.labLayout)
        labButton = findViewById<Button>(R.id.labButton)

        // fab(์ผ์‹œ์ •์ง€/์‹œ์ž‘) ๋ฒ„ํŠผ ์ƒ๋žต

        labButton.setOnClickListener { // ๋žฉํƒ€์ž„ ๋ฒ„ํŠผ
            recordLapTime()
        }
    }

    // ํƒ€์ด๋จธ ๋ฉˆ์ถค ์ƒ๋žต
    // ํƒ€์ด๋จธ ์‹œ์ž‘ ์ƒ๋žต

    private fun recordLapTime(){        // ์‹œ๊ฐ„ ๊ธฐ๋ก
        val lapTime = this.time         // ํ˜„์žฌ ์‹œ๊ฐ„์„ ๋ณ€์ˆ˜์— ์ €์žฅ(time์— ์ €์žฅ๋œ ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ)
        val textView = TextView(this)       // textView ์ƒ์„ฑ(์ƒ์„ฑ์ž ํ•จ์ˆ˜)
        textView.text = "$lap LAB : ${lapTime/100}.${lapTime%100}"  // ์‹ค์ œ ๊ฐ’ ์“ฐ๊ธฐ

        labLayout.addView(textView, 0)      // ๋ฌด์กฐ๊ฑด ์œ„๋กœ ์ƒ๊น€(2LAB์ด ์ƒ์„ฑ๋˜๋ฉด 1LAB์ด ์•„๋ž˜๋กœ ๋ฐ€๋ฆผ)
        lap++
    }
}

 

ํƒ€์ด๋จธ ์ดˆ๊ธฐํ™” ๊ตฌํ˜„

class MainActivity : AppCompatActivity() {

    private var time = 0;   // ์‹œ๊ฐ„์„ ๊ณ„์‚ฐํ•  ๋ณ€์ˆ˜
    private var timerTask: Timer? = null
    private var isRunning = false       // ์ž‘๋™ ์ค‘: ์ฒ˜์Œ์€ false(์ž‘๋™X)
    private var lap = 1     // ๋ช‡๋ฒˆ์งธ ๋žฉ์ธ์ง€ ํ‘œ์‹œํ•˜๋Š” ๋ณ€์ˆ˜(๋ฌด์กฐ๊ฑด 1๋ถ€ํ„ฐ ์‹œ์ž‘)

    lateinit var fab: FloatingActionButton
    lateinit var secTextView: TextView
    lateinit var milliTextView: TextView
    lateinit var labLayout: LinearLayout
    lateinit var labButton: Button
    lateinit var resetFab: FloatingActionButton

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

        fab = findViewById<FloatingActionButton>(R.id.fab)
        secTextView = findViewById<TextView>(R.id.secTextView)
        milliTextView = findViewById<TextView>(R.id.milliTextView)
        labLayout = findViewById<LinearLayout>(R.id.labLayout)
        labButton = findViewById<Button>(R.id.labButton)
        resetFab = findViewById<FloatingActionButton>(R.id.resetFab)

        // ์œ„์ ฏ ์ƒ๋žต

        resetFab.setOnClickListener {   // ์ดˆ๊ธฐํ™” ๋ฒ„ํŠผ
            reset()
        }
    }

    // ํ•จ์ˆ˜ ์ƒ๋žต

    private fun reset(){        // ํƒ€์ด๋จธ ์ดˆ๊ธฐํ™”
        timerTask?.cancel()     // ํƒ€์ด๋จธ์˜ ๋ชจ๋“  ๋™์ž‘ ๋ฉˆ์ถ”๊ธฐ

        // ๋ชจ๋“  ๋ณ€์ˆ˜ ์ดˆ๊ธฐํ™”
        time = 0
        isRunning = false
        fab.setImageResource(R.drawable.baseline_play_arrow_24)
        secTextView.text = "0"
        milliTextView.text = "00"

        labLayout.removeAllViews()
        lap = 1
    }
}

 

 

์ตœ์ข…

package com.example.stopwatch

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import com.google.android.material.floatingactionbutton.FloatingActionButton
import java.util.Timer
import kotlin.concurrent.timer

class MainActivity : AppCompatActivity() {

    private var time = 0;   // ์‹œ๊ฐ„์„ ๊ณ„์‚ฐํ•  ๋ณ€์ˆ˜
    private var timerTask: Timer? = null
    private var isRunning = false       // ์ž‘๋™ ์ค‘: ์ฒ˜์Œ์€ false(์ž‘๋™X)
    private var lap = 1     // ๋ช‡๋ฒˆ์งธ ๋žฉ์ธ์ง€ ํ‘œ์‹œํ•˜๋Š” ๋ณ€์ˆ˜(๋ฌด์กฐ๊ฑด 1๋ถ€ํ„ฐ ์‹œ์ž‘)

    lateinit var fab: FloatingActionButton
    lateinit var secTextView: TextView
    lateinit var milliTextView: TextView
    lateinit var labLayout: LinearLayout
    lateinit var labButton: Button
    lateinit var resetFab: FloatingActionButton

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

        fab = findViewById<FloatingActionButton>(R.id.fab)
        secTextView = findViewById<TextView>(R.id.secTextView)
        milliTextView = findViewById<TextView>(R.id.milliTextView)
        labLayout = findViewById<LinearLayout>(R.id.labLayout)
        labButton = findViewById<Button>(R.id.labButton)
        resetFab = findViewById<FloatingActionButton>(R.id.resetFab)

        fab.setOnClickListener {    // fab(์ผ์‹œ์ •์ง€/์‹œ์ž‘) ๋ฒ„ํŠผ
            isRunning = !isRunning         // ๋ฌด์กฐ๊ฑด ์ผ์‹œ์ •์ง€ ์•„๋‹ˆ๋ฉด ์‹œ์ž‘ ์„œ๋กœ ๋ฐ˜๋Œ€๋กœ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก

            if(isRunning){  // ์ž‘๋™O
                start()     // timer ์‹œ์ž‘
            }
            else{           // ์ž‘๋™X
                pause()     // timer ๋ฉˆ์ถค
            }
        }

        labButton.setOnClickListener { // ๋žฉํƒ€์ž„ ๋ฒ„ํŠผ
            recordLapTime()
        }

        resetFab.setOnClickListener {   // ์ดˆ๊ธฐํ™” ๋ฒ„ํŠผ
            reset()
        }
    }

    private fun pause(){    // ํƒ€์ด๋จธ ๋ฉˆ์ถค
        fab.setImageResource(R.drawable.baseline_play_arrow_24)  // fab ๋ˆ„๋ฅด๋ฉด fab์˜ ์ด๋ฏธ์ง€(์‹œ์ž‘) ๋ณ€๊ฒฝ
        timerTask?.cancel()      // ํ•˜๋Š” ์ผ ๋ฉˆ์ถ”๊ธฐ
    }

    private fun start(){    // ํƒ€์ด๋จธ ์‹œ์ž‘
        fab.setImageResource(R.drawable.baseline_pause_24)  // fab ๋ˆ„๋ฅด๋ฉด fab์˜ ์ด๋ฏธ์ง€(์ผ์‹œ์ •์ง€) ๋ณ€๊ฒฝ

        timerTask = timer(period=10){   // 0.01์ดˆ
            time++
            val sec = time / 100            // ์ดˆ๋กœ ๋ณ€ํ™˜
            val milli = time % 100          // ๋ฐ€๋ฆฌ์ดˆ๋กœ ๋ณ€ํ™˜
            runOnUiThread {                 // UI ๊ฐฑ์‹ 
                secTextView.text = "$sec"
                milliTextView.text = "$milli"
            }
        }
    }

    private fun recordLapTime(){        // ์‹œ๊ฐ„ ๊ธฐ๋ก
        val lapTime = this.time         // ํ˜„์žฌ ์‹œ๊ฐ„์„ ๋ณ€์ˆ˜์— ์ €์žฅ(time์— ์ €์žฅ๋œ ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ)
        val textView = TextView(this)       // textView ์ƒ์„ฑ(์ƒ์„ฑ์ž ํ•จ์ˆ˜)
        textView.text = "$lap LAB : ${lapTime/100}.${lapTime%100}"  // ์‹ค์ œ ๊ฐ’ ์“ฐ๊ธฐ

        labLayout.addView(textView, 0)      // ๋ฌด์กฐ๊ฑด ์œ„๋กœ ์ƒ๊น€(2LAB์ด ์ƒ์„ฑ๋˜๋ฉด 1LAB์ด ์•„๋ž˜๋กœ ๋ฐ€๋ฆผ)
        lap++
    }

    private fun reset(){        // ํƒ€์ด๋จธ ์ดˆ๊ธฐํ™”
        timerTask?.cancel()     // ํƒ€์ด๋จธ์˜ ๋ชจ๋“  ๋™์ž‘ ๋ฉˆ์ถ”๊ธฐ

        // ๋ชจ๋“  ๋ณ€์ˆ˜ ์ดˆ๊ธฐํ™”
        time = 0
        isRunning = false
        fab.setImageResource(R.drawable.baseline_play_arrow_24)
        secTextView.text = "0"
        milliTextView.text = "00"

        labLayout.removeAllViews()
        lap = 1
    }
}

 

 

์ •๋ฆฌ

- ์ด๋ฏธ์ง€๋ฅผ ์˜ฌ๋ฆด ์ˆ˜ ์žˆ๋Š” ๋‘ฅ๊ทผ ๋ฒ„ํŠผ : FloatingActionButton

- ๋ฒกํ„ฐ ์ด๋ฏธ์ง€๋ฅผ ์•„์ด์ฝ˜์œผ๋กœ ์„ค์ •ํ•˜๊ณ  backgroundTint ์†์„ฑ์œผ๋กœ ๋ฐฐ๊ฒฝ์ƒ‰์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๊ณ  ์•„์ด์ฝ˜ ์ƒ‰์ƒ์€ xml์—์„œ app:tint ์†์„ฑ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Œ

- timer์™€ runOnUiThread๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ณ„์‚ฐํ•˜๋ฉด์„œ UI๋ฅผ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ์Œ

- ์ฝ”ํ‹€๋ฆฐ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ทฐ๋ฅผ ๋™์ ์œผ๋กœ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Œ

 

 

 

์‘์šฉ - ํƒ€์ด๋จธ ๋งŒ๋“ค๊ธฐ

 

package com.example.timerapp

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import java.util.Timer
import kotlin.concurrent.timer

class MainActivity : AppCompatActivity() {

    private var time = 0;
    private var timerTask: Timer? = null

    lateinit var secTextView: TextView
    lateinit var milliTextView: TextView
    lateinit var secEditText: EditText
    lateinit var setButton: Button

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

        secTextView = findViewById<TextView>(R.id.secTextView)
        milliTextView = findViewById<TextView>(R.id.milliTextView)
        secEditText = findViewById<EditText>(R.id.secEditText)
        setButton = findViewById<Button>(R.id.setButton)

        setButton.setOnClickListener {
            time = secEditText.text.toString().toInt() * 100      // 100 ๊ณฑํ•˜๊ธฐ!! (์•„๋‹ˆ๋ฉด millisec์œผ๋กœ ๊ณ„์‚ฐ๋จ)
            start()
        }
    }

    private fun pause(){
        timerTask?.cancel()
    }

    private fun start(){
        timerTask = timer(period=10){
            time--
            val sec = time / 100
            val milli = time % 100

            if(sec == 0 && milli == 0) timerTask?.cancel()
            runOnUiThread {
                secTextView.text = "$sec"
                milliTextView.text = "$milli"
            }
        }
    }
}