k906506 / AOS-Timer

๐Ÿ›  SeekBar์™€ SoundPool์„ ํ™œ์šฉํ•œ ์•Œ๋ฆผ ๊ธฐ๋Šฅ์ด ์žˆ๋Š” ํƒ€์ด๋จธ

Home Page:https://www.notion.so/codekodo/Android-Timer-App-3f44f21aa51c48ec90ff4ec60e90f4a0

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ํ‚ค์›Œ๋“œ

  • Constraint View
  • SeekBar
  • CountDownTimer

๊ตฌํ˜„ ๋ชฉ๋ก

  • ํƒ€์ด๋จธ ์‹œ๊ฐํ™”
  • ํƒ€์ด๋จธ ์ž‘๋™, ์ข…๋ฃŒ์‹œ ์†Œ๋ฆฌ ์•Œ๋ฆผ
  • ์ƒํƒœ ๊ด€๋ฆฌ

๊ฐœ๋ฐœ ๊ณผ์ • (๋…ธ์…˜์—์„œ ํ™•์ธ)

1. ๊ธฐ๋ณธ UI ์„ค์ •ํ•˜๊ธฐ

TextView

์šฐ์„  Constraint View๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ถ„๊ณผ ์ดˆ๋ฅผ ํ‘œ์‹œํ–ˆ๋‹ค. ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ ์ฒ˜์Œ ์‚ฌ์šฉํ•œ ๊ฒƒ์€ Constraint Chain Style ๊ณผ BaseLine ์ด๋‹ค. ๋‘ ๊ฐ€์ง€๋ฅผ ์ ์šฉํ•œ ์ด์œ ๋Š” ๋ถ„๊ณผ ์ดˆ๋ฅผ ํ‘œ์‹œํ•˜๋Š” TextView๊ฐ€ ๋ถ™์–ด์žˆ๊ฒŒ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ์˜€๋‹ค.

image

์ฒซ ๋ฒˆ์งธ ์‚ฌ์ง„์€ Constraint Chain๊ณผ BaseLine์„ ์ ์šฉํ•˜๊ธฐ ์ „์ด๋‹ค. ๋ถ„๊ณผ ์ดˆ๋ฅผ ํ‘œ์‹œํ•˜๋Š” View ์‚ฌ์ด์— ์ผ์ •ํ•œ ๊ฐ„๊ฒฉ์ด ์žˆ๊ณ  Constraint๋ฅผ ์ƒ, ํ•˜, ์ขŒ, ์šฐ ๋ชจ๋‘ ์ฃผ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ค‘์•™์— ์œ„์น˜ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋‘ ๋ฒˆ์งธ ์‚ฌ์ง„์€ Constraint Chain๋งŒ ์ ์šฉํ–ˆ์„ ๋•Œ๋‹ค. ๋ถ„์„ ํ‘œ์‹œํ•˜๋Š” TextView๋ฅผ top, bottom์œผ๋กœ constraint๋ฅผ ๊ฑธ์–ด๋†”์„œ ๊ทธ๋Œ€๋กœ ์ค‘์•™์— ์œ„์น˜ํ•œ๋‹ค. ์ด๋ฅผ ์ข€ ๋” ๋ณด๊ธฐ ์ข‹๊ฒŒ ํ‘œํ˜„ํ•˜๊ณ ์ž top, bottom์˜ constraint๋ฅผ ์—†์• ๊ณ  baseline์œผ๋กœ ์ง„ํ–‰ํ–ˆ๋‹ค. ์„ธ ๋ฒˆ์งธ ์‚ฌ์ง„์ด Constraint Chain๊ณผ BaseLine์˜ ์ ์šฉ ์‚ฌ์ง„์ด๋‹ค.

ImageView

์ƒ๋‹จ์˜ ํ† ๋งˆํ†  ๊ผญ์ง€ ์ด๋ฏธ์ง€๋„ .xml์„ ์ด์šฉํ•ด์„œ ๋„ฃ์–ด์คฌ๋‹ค. ๊ฐ€๋กœ ์„ธ๋กœ 40dp ์ดํ•˜์˜ ํฌ๊ธฐ์ด๋ฉด Vector Asset ์„ ์‚ฌ์šฉํ•ด๋„ ๊ดœ์ฐฎ๋‹ค๊ณ  ํ•œ๋‹ค. ๋” ํฐ ํฌ๊ธฐ์˜ ๊ฒฝ์šฐ ์—ฌ๋Ÿฌ ์‚ฌ์ด์ฆˆ์˜ ์ด๋ฏธ์ง€ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” Image Asset ์„ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜์ž.

image

์ดํ›„ android:src ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ถˆ๋Ÿฌ์˜จ๋‹ค.

SeekBar

๋‹ค์Œ์€ ํ•˜๋‹จ์˜ SeekBar์ด๋‹ค. max ์†์„ฑ์„ ์ด์šฉํ•ด์„œ ํ‘œํ˜„ํ•  ๋ฒ”์œ„๋ฅผ ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ตฌํ˜„ํ•  ํƒ€์ด๋จธ๋Š” 60๋ถ„์ด ์ตœ๋Œ€์ด๋ฏ€๋กœ 60์„ ์ง€์ •ํ•ด์คฌ๋‹ค.

thumb ์†์„ฑ์€ ์ง€์‹œ์ž? ์™€ ๊ฐ™์€ ์—ญํ• ์„ ํ•œ๋‹ค.

image

res์˜ drawable ํด๋”์— .xml์„ ์ƒˆ๋กญ๊ฒŒ ๋งŒ๋“ค์–ด์คฌ๋‹ค.

tickMark๋Š” SeekBar์—์„œ ๊ฐ„๊ฒฉ์„ ํ‘œ์‹œํ•˜๋Š” ๋ฐฉ๋ฒ•? ์„ ์ •์˜ํ•˜๋Š” ์†์„ฑ์ด๋‹ค.

image

์—ญ์‹œ .xml์„ ์ƒˆ๋กญ๊ฒŒ ๋งŒ๋“ค์–ด์คฌ๋‹ค. ์ผ์ข…์˜ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. ๊ณต์‹ ๋ฌธ์„œ๊ฐ€ ์žˆ์–ด์„œ ๊ฐ€์ ธ์™€๋ดค๋‹ค.

์•ˆ๋“œ๋กœ์ด๋“œ ๊ณต์‹๋ฌธ์„œ

์ฝ”๋“œ

<?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">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_img_tomato_stem"
        app:layout_constraintBottom_toTopOf="@id/remainMinutesTextView"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/remainMinutesTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="00"
        android:textColor="@color/white"
        android:textSize="120sp"
        android:textStyle="bold"
        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" />

    <TextView
        android:id="@+id/remainSecondsTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="00"
        android:textColor="@color/white"
        android:textSize="70sp"
        android:textStyle="bold"
        app:layout_constraintBaseline_toBaselineOf="@id/remainMinutesTextView"
        app:layout_constraintLeft_toRightOf="@id/remainMinutesTextView"
        app:layout_constraintRight_toRightOf="parent" />

    <SeekBar
        android:id="@+id/seekBar"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="20dp"
        android:max="60"
        android:thumb="@drawable/ic_thumb"
        android:tickMark="@drawable/drawable_tick_mark"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/remainSecondsTextView" />

</androidx.constraintlayout.widget.ConstraintLayout>

2. ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ

bindView

์šฐ์„  .xml์—์„œ ์„ ์–ธํ•œ seekBar์™€ ์—ฐ๊ฒฐํ•ด์ฃผ๊ธฐ ์œ„ํ•œ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•œ๋‹ค.

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

seekBar๋Š” setOnSeekBarChangeListener ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด๋•Œ OnSeekBarChangeListener ๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š”๋‹ค.

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

            override fun onStartTrackingTouch(seekBar: SeekBar?) {
                stopCountDown()
            }

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

                if (seekBar.progress== 0) {
                    stopCountDown()
                } else {
                    startCountDown()
                }
            }
        }
    )
}

OnSeekBarChangeListener์—๋Š” 3๊ฐ€์ง€์˜ ์ƒํƒœ๊ฐ€ ์žˆ๋Š”๋ฐ onProgressChanged , onStartTrackingTouch , onStopTrackingTouch ๊ฐ€ ์žˆ๋‹ค.

  • onProgressChanged : ๋“œ๋ž˜๊ทธ๋ฅผ ํ•˜๊ณ  ์žˆ์„ ๋•Œ ์ด๋ฒคํŠธ ๋ฐœ์ƒ
  • onStartTrackingTouch : ๋“œ๋ž˜๊ทธ ์‹œ์ž‘ํ•  ๋•Œ ์ด๋ฒคํŠธ ๋ฐœ์ƒ
  • onStopTrackingTouch : ๋“œ๋ž˜๊ทธ ๋ฉˆ์ท„์„ ๋–„ ์ด๋ฒคํŠธ ๋ฐœ์ƒ

๋”ฐ๋ผ์„œ 3๊ฐ€์ง€ ์ƒํƒœ์— ๋”ฐ๋ผ ๊ฐ๊ฐ ์ด๋ฒคํŠธ๋ฅผ ๊ตฌํ˜„ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•ด์ค˜์•ผํ•œ๋‹ค.

onProgressChanged

์‚ฌ์šฉ์ž๊ฐ€ SeekBar๋ฅผ ์กฐ์ž‘ํ•˜๊ณ  ์žˆ์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ์ด๋‹ค. ์ฆ‰ ํƒ€์ด๋จธ๋ฅผ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด ์กฐ์ž‘ํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ๋“œ๋ž˜๊ทธ์— ๋”ฐ๋ผ ํ‘œ์‹œํ•˜๋Š” ์ˆซ์ž๊ฐ€ ๋ฐ”๋€Œ์–ด์•ผํ•œ๋‹ค. onProgressChanged ๋ฉ”์†Œ๋“œ๋ฅผ ๋ณด๋ฉด Progress ๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ด๋Š” max๋กœ ์ •ํ•ด์ค€ 60์˜ ๋ฒ”์œ„ ์ด๋‚ด์—์„œ 0 ~ 60์˜ ๊ฐ’์„ ๊ฐ–๋Š”๋‹ค. ์ดํ›„ milliseconds ๋‹จ์œ„๋กœ ๋ฐ”๊ฟ”์คฌ๋‹ค.

override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
    if (fromUser) {
        updateRemainTimes(progress * 60 * 1000L)
    }
}

updateRemainTimes ์—์„œ ๋ถ„๊ณผ ์ดˆ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ์ธ์ž๋กœ milliseconds ๋‹จ์œ„๋กœ ๋ฐ›์•˜์œผ๋ฏ€๋กœ ๊ณ„์‚ฐ์„ ์œ„ํ•ด ๋‹ค์‹œ ์ดˆ๋กœ ๋ณ€๊ฒฝํ•ด์คฌ๋‹ค. ์ด๋ฅผ 60์œผ๋กœ ๋‚˜๋ˆˆ ๊ฐ’์€ ๋ถ„์ด ๋˜๊ณ , 60์œผ๋กœ ๋‚˜๋ˆˆ ๋‚˜๋จธ์ง€๋Š” ์ดˆ๊ฐ€ ๋œ๋‹ค.

private fun updateRemainTimes(remainMillis: Long) {
        val remainSeconds = remainMillis / 1000
        remainMinutesTextView.text = "%02d'".format(remainSeconds / 60)
        remainSecondsTextView.text = "%02d".format(remainSeconds % 60)
    }

์ด์ œ ์‚ฌ์šฉ์ž๊ฐ€ ์‹œ๊ฐ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค. ๋‹ค์Œ์€ ์„ค์ •๋œ ์‹œ๊ฐ์ด 1์ดˆ๋งˆ๋‹ค ์ค„์–ด๋“œ๋Š” ํƒ€์ด๋จธ๋ฅผ ๋งŒ๋“ค์–ด์•ผํ•œ๋‹ค.

CountDownTimer

์šฐ์„  CountDownTimer ํด๋ž˜์Šค๋ฅผ ์‚ดํŽด๋ณด์ž.

public abstract class CountDownTimer {
    public CountDownTimer(long millisInFuture, long countDownInterval) {
        throw new RuntimeException("Stub!");
    }

    public final synchronized void cancel() {
        throw new RuntimeException("Stub!");
    }

    public final synchronized CountDownTimer start() {
        throw new RuntimeException("Stub!");
    }

    public abstract void onTick(long var1);

    public abstract void onFinish();
}

์ด์ฒ˜๋Ÿผ CountDownTimer ํด๋ž˜์Šค๋Š” ์ถ”์ƒ ํด๋ž˜์Šค์—ฌ์„œ abstract ๋กœ ์„ ์–ธํ•œ onTick ๊ณผ onFinish ๋ฅผ ๊ตฌํ˜„ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

private fun createCountDownTimer(initialMillis: Long): CountDownTimer {
        return object : CountDownTimer(initialMillis, 1000L) {
            override fun onTick(millisUntilFinished: Long) {
                updateRemainTimes(millisUntilFinished)
                updateSeekBar(millisUntilFinished)
            }

            override fun onFinish() {
                completeCountDown()
            }
        }
    }

CountDownTimer ๋Š” ์‹œ๊ฐ„ ๊ณผ ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ ์„ ์ธ์ž๋กœ ๋ฐ›๋Š”๋‹ค. onTick ์€ ์ง€์ •ํ•œ ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ๋งˆ๋‹ค ํ˜ธ์ถœ๋˜๊ณ  ๋‚จ์€ ์‹œ๊ฐ„์ด ์ธ์ž๋กœ ๋„˜์–ด์˜จ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ millisUntilFinished ์—๋Š” ๋‚จ์€ ์‹œ๊ฐ„์ด ์ €์žฅ๋˜์–ด ์žˆ๊ณ  ์ด๋ฅผ ํ™”๋ฉด์— ํ‘œ์‹œํ•ด์ฃผ๋ฉด ๋œ๋‹ค. updateRemainTimes ๋Š” ๋‚จ์€ ์‹œ๊ฐ„์„ ํ™”๋ฉด์— ๋ณด์—ฌ์ฃผ๋Š” ๋ฉ”์†Œ๋“œ์ด๊ณ ,

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

updateSeekBar ๋Š” ๋‚จ์€ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ํ•˜๋‹จ seekBar์—์„œ์˜ ํฌ์ธํ„ฐ์˜ ์œ„์น˜๋ฅผ ์ง€์ •ํ•ด์ฃผ๋Š” ๋ฉ”์†Œ๋“œ์ด๋‹ค.

์ค‘๊ฐ„ ์ ๊ฒ€

์ด๋ ‡๊ฒŒ onProgressChanged ์™€ ๊ด€๋ จ๋œ ๋ฉ”์†Œ๋“œ๋Š” ๋๋‚ฌ๋‹ค. ํ•˜์ง€๋งŒ ์•„์ง ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์€ ๋‘ ๊ฐœ์˜ ๋ฉ”์†Œ๋“œ๊ฐ€ ์žˆ๋‹ค. ๋ฐ”๋กœ onStartTrackingTouch ์™€ onEndTrackingTouch ์ด๋‹ค. ์œ„ ๋ฉ”์†Œ๋“œ๋ฅผ ์–ธ์ œ ์‚ฌ์šฉํ• ๊นŒ? ๊ฐ€์ •์„ ํ•ด๋ณด์ž. ์šฐ์„  onEndTrackingTouch์˜ ๊ฒฝ์šฐ ๋“œ๋ž˜๊ทธ๋ฅผ ์ข…๋ฃŒํ•  ๋•Œ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋‹ˆ๊นŒ ๊ฒฐ๊ตญ ์‚ฌ์šฉ์ž๊ฐ€ ์‹œ๊ฐ„์„ ์„ค์ •ํ•˜๊ณ  ์†์„ ๋•” ๋•Œ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ onEndTrackingTouch์—๋Š” ํƒ€์ด๋จธ๋ฅผ ์ž‘๋™ํ•˜๋Š” ๋ฉ”์†Œ๋“œ๊ฐ€ ๋“ค์–ด๊ฐ€์•ผ ํ•œ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด onStartTrackingTouch๋Š”? ์ฒ˜์Œ์— ์‹œ๊ฐ„์„ ์ง€์ •ํ•  ๋•Œ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๋งŒ์•ฝ ํƒ€์ด๋จธ๊ฐ€ ์ž‘๋™ ์ค‘์ด์ง€ ์•Š๋‹ค๋ฉด ๋ณ„ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š๋Š”๋‹ค. ํ•˜์ง€๋งŒ ํƒ€์ด๋จธ๊ฐ€ ์ž‘๋™ ์ค‘์ผ ๋•Œ ํ•˜๋‹จ์˜ seekBar๋ฅผ ๋งŒ์ง„๋‹ค๋ฉด? ๋‚ด๋ถ€์ ์œผ๋กœ ํƒ€์ด๋จธ๋Š” ์ž‘๋™ํ•˜๊ณ  ์žˆ๊ณ  ์ด๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” TextView ์™€ ๋ญ”๊ฐ€ ์˜ค๋ฅ˜๊ฐ€ ์ƒ๊ธธ ๊ฒƒ ๊ฐ™์ง€ ์•Š์€๊ฐ€? ์‹ค์ œ๋กœ ๋™๊ธฐํ™” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด seekBar๋ฅผ ๋งŒ์ง€๋Š” ๊ฒฝ์šฐ ํƒ€์ด๋จธ๋ฅผ ์ž ์‹œ ์ •์ง€ํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด์•ผํ•œ๋‹ค.

onStartTrackingTouch

์šฐ์„  ํƒ€์ด๋จธ๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋ถ€ํ„ฐ ์‚ดํŽด๋ณด์ž. ์œ„์—์„œ ๋งํ–ˆ๋“ฏ onStartTrackingTouch ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ seekBar๋ฅผ ๋ˆŒ๋ €์„ ๋•Œ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๋ฉ”์†Œ๋“œ์ด๋‹ค. ๋‘ ๊ฐ€์ง€ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค. ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ฒ˜์Œ ์‹คํ–‰ํ•˜๊ณ  ์‹œ๊ฐ„์„ ์„ค์ •ํ•  ๋•Œ์™€ ์ž‘๋™ ์ค‘์ธ ํƒ€์ด๋จธ์˜ ์‹œ๊ฐ„์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒฝ์šฐ. ์ฒซ ๋ฒˆ์งธ ๊ฒฝ์šฐ์ผ ๋–„๋Š” ํฐ ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ ๋‘ ๋ฒˆ์งธ ๊ฒฝ์šฐ์ผ ๋–„๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ํƒ€์ด๋จธ๊ฐ€ ์ž‘๋™ ์ค‘์ด๋ฏ€๋กœ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด๋ถ€์ ์œผ๋กœ ์ฒ˜์Œ ์„ค์ •ํ•œ ์‹œ๊ฐ„์—์„œ 1์ดˆ์”ฉ ์ค„์–ด๋“ค๊ณ  ์žˆ๋Š”๋ฐ ์ด ๋•Œ ์ƒˆ๋กœ์šด ์‹œ๊ฐ„์„ ์„ค์ •ํ•˜๋ฉด ์‹œ๊ฐ„์ด ๊ฒน์ณ๋ฒ„๋ฆฌ๋Š”? ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒƒ์ด๋‹ค. ์‹ค์ œ๋กœ ์ž‘๋™ ์ค‘์— ํƒ€์ด๋จธ ์‹œ๊ฐ„์„ ์žฌ์„ค์ •ํ•˜๋ฉด 1์ดˆ ์ •๋„ delay๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•ด์ฃผ๊ธฐ ์œ„ํ•ด ์ž‘๋™ ์ค‘์ธ ํƒ€์ด๋จธ์˜ ์‹œ๊ฐ„์„ ์žฌ์„ค์ •ํ•˜๋Š” ๊ฒฝ์šฐ ์ž ์‹œ ํƒ€์ด๋จธ๋ฅผ ๋ฉˆ์ถ”๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์คฌ๋‹ค.

override fun onStartTrackingTouch(seekBar: SeekBar?) {
                    stopCountDown()
                }

// ... ์ค‘๋žต

private fun stopCountDown() {
        currentCountDownTimer?.cancel()
        currentCountDownTimer = null
        soundPool.autoPause()
    }

CountDownTimer ํด๋ž˜์Šค๋Š” start ์™€ cancel ์˜ ๋ฉ”์†Œ๋“œ๊ฐ€ ๊ตฌํ˜„๋˜์–ด ์žˆ๋‹ค. ์ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ž‘๋™์ค‘์ธ ํƒ€์ด๋จธ๋ฅผ ์ค‘์ง€ํ•˜๊ณ  null์„ ํ†ตํ•ด ๊ธฐ์กด ํƒ€์ด๋จธ๋ฅผ ๋น„์›Œ์คฌ๋‹ค.

onStopTrackingTouch

์ด ๋ฉ”์†Œ๋“œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ seekBar์—์„œ ์†์„ ๋• ์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ์ด๋‹ค. ๋”ฐ๋ผ์„œ ํƒ€์ด๋จธ๋ฅผ ์‹œ์ž‘ํ•ด์•ผํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ํƒ€์ด๋จธ๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์คฌ๋‹ค. ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•  ์ ์€ ๋งŒ์•ฝ ์‚ฌ์šฉ์ž๊ฐ€ ํƒ€์ด๋จธ๋ฅผ 00๋ถ„์œผ๋กœ ์„ค์ •ํ•œ ๊ฒฝ์šฐ ํƒ€์ด๋จธ๊ฐ€ ์‹œ์ž‘ํ•˜๋ฉด ์•ˆ๋œ๋‹ค. ๋”ฐ๋ผ์„œ seekBar.progress == 0 ์ธ ๊ฒฝ์šฐ start๊ฐ€ ์•„๋‹Œ stop์„ ์‹คํ–‰ํ•˜๋„๋ก ๊ตฌํ˜„ํ–ˆ๋‹ค.

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

                    if (seekBar.progress == 0) {
                        stopCountDown()
                    } else {
                        startCountDown()
                    }
                }

// ... ์ค‘๋žต
private fun startCountDown() {
        currentCountDownTimer = createCountDownTimer(seekBar.progress * 60 * 1000L)
        currentCountDownTimer?.start()
        tickingSoundId?.let { soundPool.play(it, 1F, 1F, 0, -1, 1F) }
    }

์ƒํƒœ ๊ด€๋ฆฌ

์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๊ณ  ๋นŒ๋“œ๋ฅผ ํ•˜๋ฉด ์ข€ ํŠน์ดํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์–ดํ”Œ์„ ์‹คํ–‰ํ•˜๊ณ  ํ™ˆ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ์„œ ๋‚˜๊ฐ€๊ฒŒ ๋˜๋ฉด ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์‹คํ–‰๋˜๋Š”๋ฐ ์ด ๋•Œ๋„ ๊ณ„์† ํƒ€์ด๋จธ ์†Œ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ํ•˜์ง€ ์•Š์•„์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์˜€๋‹ค. ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์‹คํ–‰๋˜์ง€ ์•Š๊ฒŒ ํ•˜๋ ค๋ฉด onPause ๋ฉ”์†Œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

override fun onResume() {
        super.onResume()
        soundPool.autoResume()
    }

    override fun onPause() {
        super.onPause()
        soundPool.autoPause()
    }

    override fun onDestroy() {
        super.onDestroy()
        soundPool.release()
    }

About

๐Ÿ›  SeekBar์™€ SoundPool์„ ํ™œ์šฉํ•œ ์•Œ๋ฆผ ๊ธฐ๋Šฅ์ด ์žˆ๋Š” ํƒ€์ด๋จธ

https://www.notion.so/codekodo/Android-Timer-App-3f44f21aa51c48ec90ff4ec60e90f4a0


Languages

Language:Kotlin 100.0%