一個BUTTON,實現點選播放錄音,長按錄音

柴月和岐月發表於2017-02-19

 

因為畢業設計裡有需要用到錄音的功能,但是又要求介面比較小巧,所以思考了許久,相出個這麼辦法,讓一個BUTTON把播放和錄音的功能全部實現(當然,其他的也可以——。——~)

1.點選的話則播放/停止播放錄音

2.長按則開始錄音,手指離開則停止錄音

 

為了實現上面的效果,我們首先需要識別是點選還是長按,並且得知長按模式下按下和離開的時間。用設定setOnTouchListener的方式的話,自然可以實現,只要在按下後設定計時器就OK了,但是安卓裡GestureDetector這個類提供了onSingleTapUp()和onLongPress()這兩個方法幫助我們獲取到簡單的點選和長按事件,下面我們就測試如何利用這兩個方法來分辨點選和長按模式吧

不懂GestureDetector請手搓使用者手勢檢測-GestureDetector使用詳解

 

兩個模式的判斷

好了,請看關鍵程式碼和結果

 

首先繼承SimpleOnGestureListener並重寫上面提到的兩個方法

 

    class RecordGesture extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            Log.v("RecordGesture","onSingleTapUp");
            return super.onSingleTapUp(e);
        }

        @Override
        public void onLongPress(MotionEvent e) {
            Log.v("RecordGesture","onLongPress");
            super.onLongPress(e);
        }
    }

然後用它來設定觸控監聽,同時省略一堆東西

 

 

    private RecordGesture gesture;
    private GestureDetector detector;

省略。。。

 

 

        gesture = new RecordGesture();
        detector = new GestureDetector(context, gesture);
        setOnTouchListener(this);

省略。。。

 

 

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        detector.onTouchEvent(event);
        if (event.getAction() == MotionEvent.ACTION_UP) {
            Log.v("RecordGesture","ACTION_UP");
        }
        return true;
    }

下面附上結果圖

 

點選

長按並得知手指離開的時間


很輕易的辨別出了兩種模式,並且得知何時結束錄音,那麼就可以像這麼寫了,在函式內直接新增錄音相關的內容即可

 

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        detector.onTouchEvent(event);
        if (event.getAction() == MotionEvent.ACTION_UP && isRecording) {
            stopSoundRecord();
        }
        return true;
    }

    class RecordGesture extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            Log.v("RecordGesture","onSingleTapUp");
            if (isPlaying) {
                stopPlayRecord();
            } else {
                startPlayRecord();
            }
            return super.onSingleTapUp(e);
        }
@Override public void onLongPress(MotionEvent e) { Log.v("RecordGesture","onLongPress"); startSoundRecord(); super.onLongPress(e); } }

有人可能會問,上面的這部分程式碼為什麼不把判斷ACTION_UP放在前面呢?

detector.onTouchEvent(event);
        if (event.getAction() == MotionEvent.ACTION_UP) {
            Log.v("RecordGesture","ACTION_UP");
        }

讓我們在重寫的onTouch裡新增這句程式碼,然後觸發點選事件看看

 

 

Log.v("RecordGesture",event.getAction()+" "+System.currentTimeMillis());


 

 

 

0=ACTION_DOWN  1=ACTION_UP  2=ACTION_MOVE

可以看到在傳遞過來了幾次ACTION_MOVE後才觸發的onSingleTapUp,所以我不贊成把判斷寫在前面,因為有時會有意料之外的結果,尤其是你打算在if判斷句裡return的時候

 

專案關鍵程式碼

這章部落格我是準備寫錄音來著吧。。。。好像是,不過網上錄音的部落格太多了,就不多講了,還是直接貼程式碼吧,100多行簡單了事

 

import android.content.Context;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Vibrator;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;

import java.io.IOException;

import static android.content.Context.VIBRATOR_SERVICE;
import static android.media.MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED;
import static android.media.MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED;
import static com.sy.audiorecord.audiorecord.RecordUtils.durationToStr;
import static com.sy.audiorecord.audiorecord.RecordUtils.getRecordDuration;

public class SoundRecord extends Button implements View.OnTouchListener {
    private RecordGesture gesture;
    private GestureDetector detector;

    private Vibrator vibrator;//振動器

    private boolean isRecording = false;//是否正在錄音
    private boolean isPlaying = false;//是否正在播放錄音

    private MediaPlayer player = null;//媒體播放器
    private MediaRecorder recorder = null;//媒體錄音器

    private String filePath;//檔案路徑
    private int maxDuration = 0;//錄音最大時間限制
    private long max_filesize_bytes = 0;//錄音檔案最大大小限制

    public void setMaxDuration(int maxDuration) {
        this.maxDuration = maxDuration;
    }

    public void setMax_filesize_bytes(long max_filesize_bytes) {
        this.max_filesize_bytes = max_filesize_bytes;
    }

    public void setFilePath(String filePath) {
        this.filePath = filePath;
        setText(durationToStr(getRecordDuration(this.filePath)));
    }

    public SoundRecord(Context context) {
        super(context, null);
    }

    public SoundRecord(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        vibrator = (Vibrator) context.getSystemService(VIBRATOR_SERVICE);
        gesture = new RecordGesture();
        detector = new GestureDetector(context, gesture);
        setOnTouchListener(this);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        detector.onTouchEvent(event);
        if (event.getAction() == MotionEvent.ACTION_UP && isRecording) {
            stopSoundRecord();
        }
        return true;
    }

    class RecordGesture extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            if (isPlaying) {
                stopPlayRecord();
            } else {
                startPlayRecord();
            }
            return super.onSingleTapUp(e);
        }

        @Override
        public void onLongPress(MotionEvent e) {
            startSoundRecord();
            super.onLongPress(e);
        }
    }

    private void beforePlayStart() {
        vibrator.vibrate(100);
    }

    private void afterPlayEnd() {
        vibrator.vibrate(100);
    }

    private void startPlayRecord() {
        try {
            player = new MediaPlayer();
            player.setDataSource(filePath);
            player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    stopPlayRecord();
                }
            });
            player.prepare();
            beforePlayStart();
            player.start();
            isPlaying = true;
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    private void stopPlayRecord() {
        player.stop();
        player.release();
        player = null;
        isPlaying = false;
        afterPlayEnd();
    }

    private void beforeRecordStart() {
        vibrator.vibrate(100);
    }

    private void afterRecordEnd() {
        vibrator.vibrate(100);
        setText(durationToStr(getRecordDuration(filePath)));
    }

    private void startSoundRecord() {
        recorder = new MediaRecorder();
        recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
        recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        recorder.setOutputFile(filePath);
        recorder.setMaxDuration(maxDuration);
        recorder.setMaxFileSize(max_filesize_bytes);
        recorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
            @Override
            public void onInfo(MediaRecorder mr, int what, int extra) {
                if (MEDIA_RECORDER_INFO_MAX_DURATION_REACHED == what || MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED == what) {stopSoundRecord();
 } } }); try { recorder.prepare(); beforeRecordStart(); recorder.start(); isRecording = true; } catch (IOException e1) { e1.printStackTrace(); } } private void stopSoundRecord() { recorder.release(); recorder = null; isRecording = false; afterRecordEnd(); }}

 

RecordUtils類

 

 

public class RecordUtils {
    public static int getRecordDuration(String filePath) {
        int duration = -1;
        //如果檔案不存在就返回-1
        File file = new File(filePath);
        if (!file.exists())
            return duration;
        //如果檔案存在但是是流媒體直播的內容則也會返回-1
        MediaPlayer player = new MediaPlayer();
        try {
            player.setDataSource(filePath);
            player.prepare();
            duration = player.getDuration();
            player.reset();
            player.release();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return duration;
    }

    public static String durationToStr(int duration) {
        //如果傳進來的時長為-1則返回代表沒有內容的字串"..:.."
        if (-1 == duration)
            return "..:..";
        //時長不為1的話,則按"分:秒"的字串形式組合字串傳出
        StringBuilder builder = new StringBuilder();
        float sumSecond = duration / 1000;
        int minute = (int) (sumSecond / 60);
        int second = (int) (sumSecond % 60);
        builder.append(minute).append(":").append(second);
        return builder.toString();
    }
}

用的話,就這麼用

 

        record= (SoundRecord) findViewById(R.id.record);
        record.setFilePath(Environment.getExternalStorageDirectory().getPath()+
                File.separatorChar+"123"+".mp3");

也不要忘了新增許可權

 

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.VIBRATE" />這個是振動器的許可權

 

相關文章