Android 프로그레스바가 달린 녹음기를 만들자 4

안녕하세요
오늘은 녹음기 마지막 시간으로
재생중 일시 정지 기능을 넣도록 하겠습니다.

이 기능을 넣기 위해서는 MediaPlayer를 초기화 하는 부분을 분리해야 합니다.

소스 수정은 ProgressRecorder.java 파일과 main.xml 만 수정하겠습니다.

먼저 main.xml 입니다.
재생 정지 버튼을 추가하였습니다.
****************************** main.xml ********************************
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >
 <!-- progress bar --> 
 <LinearLayout
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:gravity="center_vertical|center_horizontal"
  android:background="@null">
  <SeekBar
   android:id="@+id/recProgressBar"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:layout_gravity="center_vertical|center_horizontal"
   android:max="60000"
   android:padding="10dp"
   android:progress="0"/>
  
  <RelativeLayout
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:gravity="center_vertical"
      android:orientation="vertical"
      android:background="@null">

   <TextView
    android:id="@+id/tvRecStartPoint"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical|left"
    android:layout_alignParentLeft="true"
    android:textSize="12sp"
    android:textColor="#FFFFFF"
    android:text="00:00"
    />
   <TextView
    android:id="@+id/tvRecMaxPoint"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical|right"
    android:layout_alignParentRight="true"
    android:textSize="12sp"
    android:textColor="#FFFFFF"
    android:text="01:00"
    />
  </RelativeLayout>
 </LinearLayout>

 <LinearLayout
  android:orientation="horizontal"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:gravity="center_vertical|center_horizontal"
  android:background="@null"> 
  <Button
      android:id="@+id/btnStartRec"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:textSize="14sp"
      android:text="Rec" />
 </LinearLayout> 


 <!-- progress bar --> 
 <LinearLayout
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:gravity="center_vertical|center_horizontal"
  android:background="@null">
  <SeekBar
   android:id="@+id/playProgressBar"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:layout_gravity="center_vertical|center_horizontal"
   android:max="60000"
   android:padding="10dp"
   android:progress="0"/>
  
  <RelativeLayout
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:gravity="center_vertical"
      android:orientation="vertical"
      android:background="@null">

   <TextView
    android:id="@+id/tvPlayStartPoint"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical|left"
    android:layout_alignParentLeft="true"
    android:textSize="12sp"
    android:textColor="#FFFFFF"
    android:text="00:00"
    />
   <TextView
    android:id="@+id/tvPlayMaxPoint"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical|right"
    android:layout_alignParentRight="true"
    android:textSize="12sp"
    android:textColor="#FFFFFF"
    android:text="01:00"
    />
  </RelativeLayout>
 </LinearLayout>

 <LinearLayout
  android:orientation="horizontal"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:gravity="center_vertical|center_horizontal"
  android:background="@null"> 
  <Button
      android:id="@+id/btnStartPlay"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:textSize="14sp"
      android:text="Play" />
  <Button
      android:id="@+id/btnStopPlay"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:textSize="14sp"
      android:text="Stop" />

 </LinearLayout> 
</LinearLayout>
************************************************************************

다음은 ProgressRecorder.java 파일입니다.
별로 어려운것은 없으니 찬찬히 살펴보시면 될듯 합니다.
이런 프로그램에서 중요한 것은 경우의 수에 따른 제어가 되겠습니다.
재생중에 재생시작 버튼은 pause로 바뀌어야 하고 pause를 누르면 다시
start 버튼으로 바뀌고 뭐 이런거죠
한번 살펴보시기 바랍니다.
*********************** ProgressRecorder.java **************************
package com.android.progressrecorder;

import java.io.IOException;

import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

public class ProgressRecorder extends Activity implements View.OnClickListener, OnCompletionListener
{
  private static final int REC_STOP = 0;
  private static final int RECORDING = 1;
  private static final int PLAY_STOP = 0;
  private static final int PLAYING = 1;
  private static final int PLAY_PAUSE = 2;
 
  private MediaRecorder mRecorder = null;
  private MediaPlayer mPlayer = null;
  private int mRecState = REC_STOP;
  private int mPlayerState = PLAY_STOP;
  private SeekBar mRecProgressBar, mPlayProgressBar;
  private Button mBtnStartRec, mBtnStartPlay, mBtnStopPlay;
  private String mFilePath, mFileName = "test.amr";
  private TextView mTvPlayMaxPoint;
 
  private int mCurRecTimeMs = 0;
  private int mCurProgressTimeDisplay = 0;
 
  // 녹음시 SeekBar처리
  Handler mProgressHandler = new Handler()
  {
    public void handleMessage(Message msg)
    {
      mCurRecTimeMs = mCurRecTimeMs + 100;
      mCurProgressTimeDisplay = mCurProgressTimeDisplay + 100;
     
      // 녹음시간이 음수이면 정지버튼을 눌러 정지시켰음을 의미하므로
      // SeekBar는 그대로 정지시키고 레코더를 정지시킨다.
      if (mCurRecTimeMs < 0)
      {}
      // 녹음시간이 아직 최대녹음제한시간보다 작으면 녹음중이라는 의미이므로
      // SeekBar의 위치를 옮겨주고 0.1초 후에 다시 체크하도록 한다.
      else if (mCurRecTimeMs < 60000)
      {
        mRecProgressBar.setProgress(mCurProgressTimeDisplay);
        mProgressHandler.sendEmptyMessageDelayed(0, 100);
      }
      // 녹음시간이 최대 녹음제한 시간보다 크면 녹음을 정지 시킨다.
      else
      {
        mBtnStartRecOnClick();
      }
    }
  };
 
  // 재생시 SeekBar 처리
  Handler mProgressHandler2 = new Handler()
  {
    public void handleMessage(Message msg)
    {
      if (mPlayer == null) return;
     
      try
      {
        if (mPlayer.isPlaying())
        {
          mPlayProgressBar.setProgress(mPlayer.getCurrentPosition());
          mProgressHandler2.sendEmptyMessageDelayed(0, 100);
        }
      }
      catch (IllegalStateException e)
      {}
      catch (Exception e)
      {}
    }
  };
 
  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
   
    // SD카드에 디렉토리를 만든다.
    mFilePath = SunUtil.makeDir("progress_recorder");
   
    mBtnStartRec = (Button) findViewById(R.id.btnStartRec);
    mBtnStartPlay = (Button) findViewById(R.id.btnStartPlay);
    mBtnStopPlay = (Button) findViewById(R.id.btnStopPlay);
    mRecProgressBar = (SeekBar) findViewById(R.id.recProgressBar);
    mPlayProgressBar = (SeekBar) findViewById(R.id.playProgressBar);
    mTvPlayMaxPoint = (TextView) findViewById(R.id.tvPlayMaxPoint);
   
    mBtnStartRec.setOnClickListener(this);
    mBtnStartPlay.setOnClickListener(this);
    mBtnStopPlay.setOnClickListener(this);
  }
 
  // 버튼의 OnClick 이벤트 리스너
  public void onClick(View v)
  {
    switch(v.getId())
    {
      case R.id.btnStartRec:
        mBtnStartRecOnClick();
        break;
      case R.id.btnStartPlay:
        mBtnStartPlayOnClick();
        break;
      case R.id.btnStopPlay:
        mBtnStopPlayOnClick();
        break;
      default:
        break;
    }
  }
 
  private void mBtnStartRecOnClick()
  {
    if (mRecState == REC_STOP)
    {
      mRecState = RECORDING;
      startRec();
      updateUI();
    }
    else if (mRecState == RECORDING)
    {
      mRecState = REC_STOP;
      stopRec();
      updateUI();
    }
  }
 
  // 녹음시작
  private void startRec()
  {
    mCurRecTimeMs = 0;
    mCurProgressTimeDisplay = 0;
   
    // SeekBar의 상태를 0.1초후 체크 시작
    mProgressHandler.sendEmptyMessageDelayed(0, 100);
   
    if (mRecorder == null)
    {
      mRecorder = new MediaRecorder();
      mRecorder.reset();
    }
    else
    {
      mRecorder.reset();
    }
   
    try
    {
      mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
      mRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
      mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
      mRecorder.setOutputFile(mFilePath + mFileName);
      mRecorder.prepare();
      mRecorder.start();
    }
    catch (IllegalStateException e)
    {
      Toast.makeText(this, "IllegalStateException", 1).show();
    }
    catch (IOException e)
    {
      Toast.makeText(this, "IOException", 1).show();
    }
  }
 
  // 녹음정지
  private void stopRec()
  {
    try
    {
      mRecorder.stop();
    }
    catch(Exception e)
    {}
    finally
    {
      mRecorder.release();
      mRecorder = null;
    }
   
    mCurRecTimeMs = -999;
    // SeekBar의 상태를 즉시 체크 
    mProgressHandler.sendEmptyMessageDelayed(0, 0);
  }
 
  private void mBtnStartPlayOnClick()
  {
    if (mPlayerState == PLAY_STOP)
    {
      mPlayerState = PLAYING;
      initMediaPlayer();
      startPlay();
      updateUI();
    }
    else if (mPlayerState == PLAYING)
    {
      mPlayerState = PLAY_PAUSE;
      pausePlay();
      updateUI();
    }
    else if (mPlayerState == PLAY_PAUSE)
    {
      mPlayerState = PLAYING;
      startPlay();
      updateUI();
    }
  }
 
  private void mBtnStopPlayOnClick()
  {
    if (mPlayerState == PLAYING || mPlayerState == PLAY_PAUSE)
    {
      mPlayerState = PLAY_STOP;
      stopPlay();
      releaseMediaPlayer();
      updateUI();     
    }
  }
 
  private void initMediaPlayer()
  {
    // 미디어 플레이어 생성
    if (mPlayer == null)
      mPlayer = new MediaPlayer();
    else
      mPlayer.reset();
   
    mPlayer.setOnCompletionListener(this);
    String fullFilePath = mFilePath + mFileName;
   
    try
    {
      mPlayer.setDataSource(fullFilePath);
      mPlayer.prepare();  
      int point = mPlayer.getDuration();
      mPlayProgressBar.setMax(point);
     
      int maxMinPoint = point / 1000 / 60;
      int maxSecPoint = (point / 1000) % 60;
      String maxMinPointStr = "";
      String maxSecPointStr = "";
     
      if (maxMinPoint < 10)
        maxMinPointStr = "0" + maxMinPoint + ":";
      else
        maxMinPointStr = maxMinPoint + ":";
     
      if (maxSecPoint < 10)
        maxSecPointStr = "0" + maxSecPoint;
      else
        maxSecPointStr = String.valueOf(maxSecPoint);
     
      mTvPlayMaxPoint.setText(maxMinPointStr + maxSecPointStr);
     
      mPlayProgressBar.setProgress(0);
    }
    catch(Exception e)
    {
      Log.v("ProgressRecorder", "미디어 플레이어 Prepare Error ==========> " + e);
    }
  }
 
  // 재생 시작
  private void startPlay()
  {
    Log.v("ProgressRecorder", "startPlay().....");
   
    try
    {
      mPlayer.start();
     
      // SeekBar의 상태를 0.1초마다 체크     
      mProgressHandler2.sendEmptyMessageDelayed(0, 100);
    }
    catch (Exception e)
    {
      e.printStackTrace();
      Toast.makeText(this, "error : " + e.getMessage(), 0).show();
    }
  }
 
  private void pausePlay()
  {
    Log.v("ProgressRecorder", "pausePlay().....");
   
    // 재생을 일시 정지하고
    mPlayer.pause();
   
    // 재생이 일시정지되면 즉시 SeekBar 메세지 핸들러를 호출한다.
    mProgressHandler2.sendEmptyMessageDelayed(0, 0);
  }
 
  private void stopPlay()
  {
    Log.v("ProgressRecorder", "stopPlay().....");
   
    // 재생을 중지하고
    mPlayer.stop();
   
    // 즉시 SeekBar 메세지 핸들러를 호출한다.
    mProgressHandler2.sendEmptyMessageDelayed(0, 0);
  }
 
  private void releaseMediaPlayer()
  {
    Log.v("ProgressRecorder", "releaseMediaPlayer().....");
    mPlayer.release();
    mPlayer = null;
    mPlayProgressBar.setProgress(0);
  }
 
  public void onCompletion(MediaPlayer mp)
  {
    mPlayerState = PLAY_STOP; // 재생이 종료됨

    // 재생이 종료되면 즉시 SeekBar 메세지 핸들러를 호출한다.
    mProgressHandler2.sendEmptyMessageDelayed(0, 0);
   
    updateUI();
  }
 
  private void updateUI()
  {
    if (mRecState == REC_STOP)
    {
      mBtnStartRec.setText("Rec");
      mRecProgressBar.setProgress(0);
    }
    else if (mRecState == RECORDING)
      mBtnStartRec.setText("Stop");
   
    if (mPlayerState == PLAY_STOP)
    {
      mBtnStartPlay.setText("Play");
      mPlayProgressBar.setProgress(0);
    }
    else if (mPlayerState == PLAYING)
      mBtnStartPlay.setText("Pause");
    else if (mPlayerState == PLAY_PAUSE)
      mBtnStartPlay.setText("Start");
  }
}
************************************************************************

이상으로 녹음기를 마치겠습니다.
다음시간에는 동적으로 컨트롤을 추가하는 방법을 알아보겠습니다.
도움이 되셨다면 리플 하나씩... ^^

by 선지헌 | 2011/04/19 11:47 | Android | 트랙백 | 덧글(9)

트랙백 주소 : http://jeehun.egloos.com/tb/4024274
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Commented by 초보자 at 2011/05/10 17:10
도움이 많이 되었어요!!! 다음강의 빨리 보고 싶어요
Commented by 선지헌 at 2011/05/10 19:48
^^ 감사합니다.
Commented by 초보자2 at 2011/05/12 23:37
SunUtill에서 에러가 납니다 ㅠ
Commented by 선지헌 at 2011/05/12 23:47
몇번째 행에서 어떤 에러가 나는지요? 이글루 편집기의 문제로 기존에 올렸던 SunUtil.java 에서 주석 부분이 깨져서 올라가더군요 그 외에는 에러 날 부분이 없을 것 같습니다만... ^^;;;
Commented by 내개념은 at 2011/05/16 13:18
저도 SunUtil에서 에러가 납니다...

mFilePath = SunUtil.makeDir("progress_recorder"); <---이부분이요...
Commented by 선지헌 at 2011/05/16 23:05
무슨 에러인지요? 혹시 퍼미션 에러 아닌가요? 퍼미션 에러라면 매니페스트 파일에 SD카드에 파일쓰기 권한을 주셔야 합니다. http://jeehun.egloos.com/4013135 게시물을 참고하세요
Commented by ㄴ ㅑㅎ ㅏ at 2012/05/30 17:16
정말 많은 도움이 되었습니다~!!!
근데 혹시 녹음을 하나 했다고 치고, 녹음 버튼을 다시 누르면
녹음 했던게 재생이 되면서, 또 녹음이 되게 할수는 없을까요??
Commented at 2012/12/05 22:57
비공개 덧글입니다.
Commented by 사과박스 at 2015/10/07 16:57
mFilePath = SunUtil.makeDir("progress_recorder"); --오류는

SunUtil.Java가 없어서 그렇습니다.
그래서 자바 클래스 추가하시면됩니다.
출처 =[http://egloos.zum.com/jeehun/v/3993673 선지헌님의 이글루 블로그]
SunUtil.java
--------------------
import java.io.File;

import android.os.Environment;

public class SunUtil
{

public static String makeDir(String dirName)
{
String mRootPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + dirName;

try
{
File fRoot = new File(mRootPath);
if (fRoot.exists() == false)
{
if (fRoot.mkdirs() == false)
{
throw new Exception("");
}
}
}
catch (Exception e)
{
mRootPath = "-1";
}

return mRootPath + "/";
}


public static String isFileExistsRename(String filePath, String fileName)
{
String returnValue = fileName;
int i = 0;

if (isFileExists(filePath + fileName))
returnValue = isFileExistsRename(filePath, fileName, i);

return returnValue;
}

private static String isFileExistsRename(String filePath, String fileName, int idx)
{
boolean b = true;
String returnValue = fileName;

while(b)
{
if (isFileExists(filePath + idx + "_" + fileName))
idx++;
else
b = false;
}

returnValue = idx + "_" + fileName;
return returnValue;
}

public static boolean isFileExists(String filePathName)
{
boolean returnValue = false;
File f = new File(filePathName);

try
{
if (f.exists())
returnValue = true;
else
returnValue = false;

f = null;
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
f = null;
}

return returnValue;
}
}

:         :

:

비공개 덧글

◀ 이전 페이지다음 페이지 ▶