Android/Side Projects

녹음기 앱 개발하기

화요밍 2022. 2. 2. 20:01
728x90
반응형
Side Projects - Recorder
소리를 녹음하고 재생하는 기본적인 녹음기 앱을 구현한 프로젝트

 

 

GitHub - hwayeon351/Recorder

Contribute to hwayeon351/Recorder development by creating an account on GitHub.

github.com


학습 회고

오늘은 녹음기 앱을 사용해서 녹음을 실행할 때 음성 크기에 따라 막대바를 생성해서 Visualization하는 기능과 녹음 시간을 TextView로 보이는 작업을 적용하였다.

이로써 기본 앱으로 제공되는 녹음기 앱 구현을 마쳤다.

기본 앱이고 기능이 간단하다 보니 구현이 쉬울줄 알았는데 MediaPlayer나 MediaRecorder를 사용하는 것과 음성 Visualization을 위해 CustomView를 구현해서 Canvas에 drawing하는 부분이 생각보다 복잡하다고 느꼈다.

그래도 새롭게 배우고 알게 된 좋은 계기였다고 생각한다.


오늘 공부한 내용

  • Custom View 생성

Android 프레임워크에 정의된 모든 View 클래스는 View를 확장한다.

따라서 커스텀 뷰를 만들기 위해서 View 클래스를 확장해야 한다.

이를 위해 Context와 AttributeSet 객체를 매개변수로 취하는 생성자를 만들어야 한다.

 

녹음기 앱에서 음성을 Visualization 해서 음성의 진폭에 따라 선을 생성해서 보여주기 위해 커스텀 뷰를 만들었다.

  • Override onSizeChanged()

Custom View를 올바르게 그리기 위해서는 크기를 알아야 한다. View 크기가 변경될 때마다 onSizeChanged()가 호출되며 해당 사이즈를 받아 View를 그리게 된다.

 

  • Override onDraw() 

커스텀 뷰를 그리기 위해서는 onDraw() 메서드를 오버라이드 해야 한다.

onDraw()의 매개변수는 Canvas 객체인데, Canvas 클래스는 뷰에서 자기 자신을 그릴 때 필요한 메서드를 제공한다.

또한, Paint 객체도 만들어 줘야 한다.

예를 들어, 선을 그리는 커스텀 뷰를 만드는 경우, Canvas는 선을 그릴 수 있는 메서드를 제공하고 Paint는 선의 색상을 정의하는 메서드를 제공하는 것이다.

즉, Canvas는 화면에 그릴 수 있는 도형을 정의하고, Paint는 그리는 도형의 색상, 스타일, 글꼴 등을 정의한다.

 

녹음기 앱에서는 amplitude를 화면 비율에 따라 선으로 그려나갈 것이기 때문에, 선의 색상과 굵기, 선의 모서리 스타일을 적용하였다.

그리고 canvas.drawLine() 메서드를 통해 선을 그려주었다.

override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)

    canvas ?: return

    val centerY = drawingHeight / 2f
    var offsetX = drawingWidth.toFloat()

    drawingAmplitudes
        .let { amplitues ->
            if (isReplaying) {
                amplitues.takeLast(replayingPosition)
            } else {
                amplitues
            }
        }
        .forEach { amplitude ->
            val lineLength = amplitude / MAX_AMPLITUDE * drawingHeight * 0.8F

            offsetX -= LINE_SPACE
            if (offsetX < 0) return@forEach

            canvas.drawLine(
                offsetX,
                centerY - lineLength / 2F,
                offsetX,
                centerY + lineLength / 2F,
                amplitudePaint
            )
        }
}

 

  • invalidate() 함수 호출
private val visualizeRepeatAction: Runnable = object : Runnable {
    override fun run() {
        if (!isReplaying) {
            val currentAmplitude = onRequestCurrentAmplitude?.invoke() ?: 0
            drawingAmplitudes = listOf(currentAmplitude) + drawingAmplitudes
        } else {
            replayingPosition++
        }

        invalidate()

        handler?.postDelayed(this, ACTION_INTERVAL)
    }
}

음성의 진폭이 변화하는 효과를 적용하기 위해서 특정 시간을 기준으로 뷰를 반복적으로 그리는 방법을 구현하였다.

invalidate() 함수를 호출하면 View가 visible인 경우, onDraw() 메서드를 재호출 할 수 있다.

핸들러의 postDelayed를 통해 ACTION_INTERVAL의 시간마다 정의한 Runnable을 Thread에 전달할 수 있다.

그때마다 invoke()를 호출해서 MainActivity에서 onRequestCurrentAmplitude를 현재 녹음기의 최대 진폭 recorder.maxAmplitude amplitude으로 받아오도록 할 수 있다.

이렇게 새롭게 추가된 진폭값을 받아와 drawingAmplitudes 리스트에 담고 invalidate() 함수를 호출함으로써 다시 onDraw()가 호출되고 정의한 ACTION_INTERVAL마다 새롭게 뷰를 그릴 수 있다.

MainActivity

 

 

https://developer.android.com/training/custom-views/create-view?hl=ko 

 

뷰 클래스 만들기  |  Android 개발자  |  Android Developers

잘 설계된 맞춤 뷰는 잘 설계된 다른 클래스와 매우 유사합니다. 즉, 사용하기 쉬운 인터페이스로 일련의 특정 기능을 캡슐화하고 CPU 및 메모리를 효율적으로 사용하는 등의 역할을 합니다. 그

developer.android.com

 

 

728x90
반응형

'Android > Side Projects' 카테고리의 다른 글

푸쉬 알림 수신기 앱 개발하기  (0) 2022.02.04
Simple Web Browser 앱 개발하기  (0) 2022.02.03
녹음기 앱 개발하기  (0) 2022.02.01
녹음기 앱 개발하기  (0) 2022.01.31
Pomodoro Timer 앱 개발하기  (0) 2022.01.30