android seek Bar_sound Pool

UI

ChainStyle

한 라인에서 요소의 가장 첫번째 속성에 설정 할 수 있다. 요소들의 간격을 조절할 수 있다. 아래 이미지 참고

image

seekBar

손가락으로 눌러서 조절 할 수 있는 progress bar를 표현 할 수 있다.

<SeekBar
    android:id="@+id/seekBar"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginHorizontal="20dp"
    android:max="60"
    android:progressDrawable="@color/transparent"
    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/remainMinutesTextView" />

위의 소스코드에서 thumb은 현재 값을 표시, tickmark는 값의 하나하나 위치를 표시해준다.

themes

안드로이드는 처음 OS에서 테마를 뿌려주고 그위에 view 스타일을 바인딩한다. 따라서 처음에 테마의 background를 지정하면 첫 앱 스타트 시 배경색이 하얗게 되었다가 변경되는것을 처음부터 원하는 색으로 설정할 수 있다.

<!-- themes.xml-->
<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.Chapter06" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <!-- Status bar color. -->
        <item name="android:statusBarColor">@color/pomodoro_red</item>
        <!-- Customize your theme here. -->
        <item name="android:windowBackground">@color/pomodoro_red</item>
    </style>
</resources>

Back

CountDownTimer

CountDownTimer를 상속받아 override하여 사용 한다. 인자로는 long타입의 시작 숫자와 카운트다운할 인터벌 설정 값을 준다.

private fun createCountDownTimer(initialMillis: Long) =
    object : CountDownTimer(initialMillis, 1000L) {
        override fun onTick(p0: Long) {
            updateRemainTime(p0) //유닛 하나가 실행될때 인자로 알려줌
            updateSeekBar(p0)
        }

        override fun onFinish() {
            completeCountDown()

        }

    }

//남은 시간을 받아서 textview에 표시
private fun updateRemainTime(remainMills: Long) {

    val remainSeconds = remainMills / 1000

    remainMinutesTextView.text = "%02d'".format(remainSeconds / 60)
    remainSecondsTextView.text = "%02d".format(remainSeconds % 60)
}

//seekBar에 남은 시간을 표시
private fun updateSeekBar(remainMills: Long) {
    seekBar.progress = (remainMills / 1000 / 60).toInt()
}

//카운트 다운 완료
private fun completeCountDown() {
    updateRemainTime(0)
    updateSeekBar(0)

    soundPool.autoPause()
    //let 람다식을 활용하여 null safeCall
    bellSoundId?.let {
        soundPool.play(it, 1F, 1F, 0, 0, 1F)
    }
}

SeekBar

progress Bar에서 사용자가 설정한 값이 변경 될때마다 Listener로 전달 받는다.

private fun bindViews() {
    seekBar.setOnSeekBarChangeListener(
        object : SeekBar.OnSeekBarChangeListener {
            //변경 될때 마다 값을 textView에 표시
            override fun onProgressChanged(
                seekBar: SeekBar?,
                progress: Int,
                fromUser: Boolean
            ) {
                if (fromUser) {
                    updateRemainTime(progress * 60 * 1000L)
                }
            }
            //bar를 조절할때 countDown하지 않도록 함
            override fun onStartTrackingTouch(seekBar: SeekBar?) {
                stopCountDown()
            }
            //bar 조절을 멈췄을때 카운트 다운 시작 및 0일때 체크
            override fun onStopTrackingTouch(seekBar: SeekBar?) {
                seekBar ?: return
                if(seekBar.progress == 0){
                    stopCountDown()
                }else {
                    startCountDown()
                }
            }
        }
    )
}

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

private fun startCountDown(){
    currentCountDownTimer = createCountDownTimer(seekBar.progress * 60 * 1000L)
    currentCountDownTimer?.start()

    tickingSoundId?.let {
        soundPool.play(it, 1F, 1F, 0, -1, 1F)
    }
}

soundPool

메모리에 작은 음악 리소스를 사용하여 소리를 내게 해준다.

private fun initSounds() {
    tickingSoundId = soundPool.load(this, R.raw.timer_ticking, 1)
    bellSoundId = soundPool.load(this, R.raw.timer_bell, 1)
}

id를 부여하기 위해 파일을 load하여 선언 한다.

소리를 play 하기 위해서는 아래와같이 설정하며 인자는 soundId, 왼쪽, 오른쪽 소리 크기, 스트림 우선순위, 반복, 재생속도를 의미한다.

tickingSoundId?.let {
    soundPool.play(it, 1F, 1F, 0, -1, 1F)
}

또한 Activity LifeCycle에 맞추어 pause와 release해준다.

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

    bindViews()
    initSounds()
}
//화면 다시 켰을때
override fun onResume() {
    super.onResume()
    soundPool.autoResume()
}

//앱이 화면에 안보일때
override fun onPause() {
    super.onPause()
    soundPool.autoPause()
}

//메모리에서 해제
override fun onDestroy() {
    super.onDestroy()
    soundPool.release()
}

현재 프로젝트에서는 autoPause(), autoResume()을 하였지만, 각각 ID를 통해 컨트롤 할 수 있다.