Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發--仿微信語音對講錄音

Android開發--仿微信語音對講錄音

編輯:關於Android編程

自微信出現以來取得了很好的成績,語音對講的實現更加方便了人與人之間的交流。今天來實踐一下微信的語音對講的錄音實現,這個也比較容易實現。在此,我將該按鈕封裝成為一個控件,並通過策略模式的方式實現錄音和界面的解耦合,以方便我們在實際情況中對錄音方法的不同需求(例如想要實現wav格式的編碼時我們也就不能再使用MediaRecorder,而只能使用AudioRecord進行處理)。
效果圖:
title=

實現思路

1.在微信中我們可以看到實現語音對講的是通過點按按鈕來完成的,因此在這裡我選擇重新自己的控件使其繼承自Button並重寫onTouchEvent方法,來實現對錄音的判斷。
2.在onTouchEvent方法中,
當我們按下按鈕時,首先顯示錄音的對話框,然後調用錄音准備方法並開始錄音,接著開啟一個計時線程,每隔0.1秒的時間獲取一次錄音音量的大小,並通過Handler根據音量大小更新Dialog中的顯示圖片;
當我們移動手指時,若手指向上移動距離大於50,在Dialog中顯示松開手指取消錄音的提示,並將isCanceled變量(表示我們最後是否取消了錄音)置為true,上移動距離小於20時,我們恢復Dialog的圖片,並將isCanceled置為false;
當抬起手指時,我們首先關閉錄音對話框,接著調用錄音停止方法並關閉計時線程,然後我們判斷是否取消錄音,若是的話則刪除錄音文件,否則判斷計時時間是否太短,最後調用回調接口中的recordEnd方法。
3.在這裡為了適應不同的錄音需求,我使用了策略模式來進行處理,將每一個不同的錄音方法視為一種不同的策略,根據自己的需要去改寫。

注意問題

1.在onTouchEvent的返回值中應該返回true,這樣才能屏蔽之後其他的觸摸事件,否則當手指滑動離開Button之後將不能在響應我們的觸摸方法。
2.不要忘記為自己的App添加權限:

    
    
    

代碼參考

RecordButton 類,我們的自定義控件,重新復寫了onTouchEvent方法

package com.example.recordtest;

import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class RecordButton extends Button {

    private static final int MIN_RECORD_TIME = 1; // 最短錄音時間,單位秒
    private static final int RECORD_OFF = 0; // 不在錄音
    private static final int RECORD_ON = 1; // 正在錄音

    private Dialog mRecordDialog;
    private RecordStrategy mAudioRecorder;
    private Thread mRecordThread;
    private RecordListener listener;

    private int recordState = 0; // 錄音狀態
    private float recodeTime = 0.0f; // 錄音時長,如果錄音時間太短則錄音失敗
    private double voiceValue = 0.0; // 錄音的音量值
    private boolean isCanceled = false; // 是否取消錄音
    private float downY;

    private TextView dialogTextView;
    private ImageView dialogImg;
    private Context mContext;

    public RecordButton(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        init(context);
    }

    public RecordButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        init(context);
    }

    public RecordButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        init(context);
    }

    private void init(Context context) {
        mContext = context;
        this.setText(按住 說話);
    }

    public void setAudioRecord(RecordStrategy record) {
        this.mAudioRecorder = record;
    }

    public void setRecordListener(RecordListener listener) {
        this.listener = listener;
    }

    // 錄音時顯示Dialog
    private void showVoiceDialog(int flag) {
        if (mRecordDialog == null) {
            mRecordDialog = new Dialog(mContext, R.style.Dialogstyle);
            mRecordDialog.setContentView(R.layout.dialog_record);
            dialogImg = (ImageView) mRecordDialog
                    .findViewById(R.id.record_dialog_img);
            dialogTextView = (TextView) mRecordDialog
                    .findViewById(R.id.record_dialog_txt);
        }
        switch (flag) {
        case 1:
            dialogImg.setImageResource(R.drawable.record_cancel);
            dialogTextView.setText(松開手指可取消錄音);
            this.setText(松開手指 取消錄音);
            break;

        default:
            dialogImg.setImageResource(R.drawable.record_animate_01);
            dialogTextView.setText(向上滑動可取消錄音);
            this.setText(松開手指 完成錄音);
            break;
        }
        dialogTextView.setTextSize(14);
        mRecordDialog.show();
    }

    // 錄音時間太短時Toast顯示
    private void showWarnToast(String toastText) {
        Toast toast = new Toast(mContext);
        View warnView = LayoutInflater.from(mContext).inflate(
                R.layout.toast_warn, null);
        toast.setView(warnView);
        toast.setGravity(Gravity.CENTER, 0, 0);// 起點位置為中間
        toast.show();
    }

    // 開啟錄音計時線程
    private void callRecordTimeThread() {
        mRecordThread = new Thread(recordThread);
        mRecordThread.start();
    }

    // 錄音Dialog圖片隨錄音音量大小切換
    private void setDialogImage() {
        if (voiceValue < 600.0) {
            dialogImg.setImageResource(R.drawable.record_animate_01);
        } else if (voiceValue > 600.0 && voiceValue < 1000.0) {
            dialogImg.setImageResource(R.drawable.record_animate_02);
        } else if (voiceValue > 1000.0 && voiceValue < 1200.0) {
            dialogImg.setImageResource(R.drawable.record_animate_03);
        } else if (voiceValue > 1200.0 && voiceValue < 1400.0) {
            dialogImg.setImageResource(R.drawable.record_animate_04);
        } else if (voiceValue > 1400.0 && voiceValue < 1600.0) {
            dialogImg.setImageResource(R.drawable.record_animate_05);
        } else if (voiceValue > 1600.0 && voiceValue < 1800.0) {
            dialogImg.setImageResource(R.drawable.record_animate_06);
        } else if (voiceValue > 1800.0 && voiceValue < 2000.0) {
            dialogImg.setImageResource(R.drawable.record_animate_07);
        } else if (voiceValue > 2000.0 && voiceValue < 3000.0) {
            dialogImg.setImageResource(R.drawable.record_animate_08);
        } else if (voiceValue > 3000.0 && voiceValue < 4000.0) {
            dialogImg.setImageResource(R.drawable.record_animate_09);
        } else if (voiceValue > 4000.0 && voiceValue < 6000.0) {
            dialogImg.setImageResource(R.drawable.record_animate_10);
        } else if (voiceValue > 6000.0 && voiceValue < 8000.0) {
            dialogImg.setImageResource(R.drawable.record_animate_11);
        } else if (voiceValue > 8000.0 && voiceValue < 10000.0) {
            dialogImg.setImageResource(R.drawable.record_animate_12);
        } else if (voiceValue > 10000.0 && voiceValue < 12000.0) {
            dialogImg.setImageResource(R.drawable.record_animate_13);
        } else if (voiceValue > 12000.0) {
            dialogImg.setImageResource(R.drawable.record_animate_14);
        }
    }

    // 錄音線程
    private Runnable recordThread = new Runnable() {

        @Override
        public void run() {
            recodeTime = 0.0f;
            while (recordState == RECORD_ON) {
                {
                    try {
                        Thread.sleep(100);
                        recodeTime += 0.1;
                        // 獲取音量,更新dialog
                        if (!isCanceled) {
                            voiceValue = mAudioRecorder.getAmplitude();
                            recordHandler.sendEmptyMessage(1);
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    };

    @SuppressLint(HandlerLeak)
    private Handler recordHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            setDialogImage();
        }
    };

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: // 按下按鈕
            if (recordState != RECORD_ON) {
                showVoiceDialog(0);
                downY = event.getY();
                if (mAudioRecorder != null) {
                    mAudioRecorder.ready();
                    recordState = RECORD_ON;
                    mAudioRecorder.start();
                    callRecordTimeThread();
                }
            }
            break;
        case MotionEvent.ACTION_MOVE: // 滑動手指
            float moveY = event.getY();
            if (downY - moveY > 50) {
                isCanceled = true;
                showVoiceDialog(1);
            }
            if (downY - moveY < 20) {
                isCanceled = false;
                showVoiceDialog(0);
            }
            break;
        case MotionEvent.ACTION_UP: // 松開手指
            if (recordState == RECORD_ON) {
                recordState = RECORD_OFF;
                if (mRecordDialog.isShowing()) {
                    mRecordDialog.dismiss();
                }
                mAudioRecorder.stop();
                mRecordThread.interrupt();
                voiceValue = 0.0;
                if (isCanceled) {
                    mAudioRecorder.deleteOldFile();
                } else {
                    if (recodeTime < MIN_RECORD_TIME) {
                        showWarnToast(時間太短  錄音失敗);
                        mAudioRecorder.deleteOldFile();
                    } else {
                        if (listener != null) {
                            listener.recordEnd(mAudioRecorder.getFilePath());
                        }
                    }
                }
                isCanceled = false;
                this.setText(按住 說話);
            }
            break;
        }
        return true;
    }

    public interface RecordListener {
        public void recordEnd(String filePath);
    }
}

Dialog布局:




    

    

錄音時間太短的Toast布局:




    

    

自定義的Dialogstyle,對話框樣式

RecordStrategy 錄音策略接口

package com.example.recordtest;

/**
 * RecordStrategy 錄音策略接口
 * @author acer
 */
public interface RecordStrategy {

    /**
     * 在這裡進行錄音准備工作,重置錄音文件名等
     */
    public void ready();
    /**
     * 開始錄音
     */
    public void start();
    /**
     * 錄音結束
     */
    public void stop();

    /**
     * 錄音失敗時刪除原來的舊文件
     */
    public void deleteOldFile();

    /**
     * 獲取錄音音量的大小
     * @return 
     */
    public double getAmplitude();

    /**
     * 返回錄音文件完整路徑
     * @return
     */
    public String getFilePath();

}

個人寫的一個錄音實踐策略

package com.example.recordtest;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.media.MediaRecorder;
import android.os.Environment;

public class AudioRecorder implements RecordStrategy {

    private MediaRecorder recorder;
    private String fileName;
    private String fileFolder = Environment.getExternalStorageDirectory()
            .getPath() + /TestRecord;

    private boolean isRecording = false;

    @Override
    public void ready() {
        // TODO Auto-generated method stub
        File file = new File(fileFolder);
        if (!file.exists()) {
            file.mkdir();
        }
        fileName = getCurrentDate();
        recorder = new MediaRecorder();
        recorder.setOutputFile(fileFolder + / + fileName + .amr);
        recorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 設置MediaRecorder的音頻源為麥克風
        recorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);// 設置MediaRecorder錄制的音頻格式
        recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);// 設置MediaRecorder錄制音頻的編碼為amr
    }

    // 以當前時間作為文件名
    private String getCurrentDate() {
        SimpleDateFormat formatter = new SimpleDateFormat(yyyy_MM_dd_HHmmss);
        Date curDate = new Date(System.currentTimeMillis());// 獲取當前時間
        String str = formatter.format(curDate);
        return str;
    }

    @Override
    public void start() {
        // TODO Auto-generated method stub
        if (!isRecording) {
            try {
                recorder.prepare();
                recorder.start();
            } catch (IllegalStateException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            isRecording = true;
        }

    }

    @Override
    public void stop() {
        // TODO Auto-generated method stub
        if (isRecording) {
            recorder.stop();
            recorder.release();
            isRecording = false;
        }

    }

    @Override
    public void deleteOldFile() {
        // TODO Auto-generated method stub
        File file = new File(fileFolder + / + fileName + .amr);
        file.deleteOnExit();
    }

    @Override
    public double getAmplitude() {
        // TODO Auto-generated method stub
        if (!isRecording) {
            return 0;
        }
        return recorder.getMaxAmplitude();
    }

    @Override
    public String getFilePath() {
        // TODO Auto-generated method stub
        return fileFolder + / + fileName + .amr;
    }

}

MainActivity

package com.example.recordtest;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;

public class MainActivity extends Activity {

    RecordButton button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = (RecordButton) findViewById(R.id.btn_record);
        button.setAudioRecord(new AudioRecorder());
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved