栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 移动开发 > Android

Android仿微信语音对讲录音功能

Android 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android仿微信语音对讲录音功能

自微信出现以来取得了很好的成绩,语音对讲的实现更加方便了人与人之间的交流。今天来实践一下微信的语音对讲的录音实现,这个也比较容易实现。在此,我将该按钮封装成为一个控件,并通过策略模式的方式实现录音和界面的解耦合,以方便我们在实际情况中对录音方法的不同需求(例如想要实现wav格式的编码时我们也就不能再使用MediaRecorder,而只能使用AudioRecord进行处理)。

效果图:

实现思路:

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,对话框样式


    @android:color/transparent
    @null
    true
    true
    true
    @android:style/Animation.Dialog
    
    false

RecordStrategy 录音策略接口

package com.example.recordtest;


public interface RecordStrategy {

  
  public void ready();
  
  public void start();
  
  public void stop();

  
  public void deleteOldFile();

  
  public double getAmplitude();

  
  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;
  }

}

源码下载:Android仿微信语音对讲录音

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持考高分网。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/154660.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号