Android有一个内置的麦克风,通过它可以捕获音频和存储,或在手机进行播放。
有很多方法可以做到这一点,但最常见的方法是通 过MediaRecorder类。
方法名 |
描述 |
setAudioSource() |
指定声音源 |
setOutputFormat() |
该方法规定了音频格式中的音频将被存储 |
setAudioEncoder() |
该方法指定要使用的音频编码器 |
setOutputFile() |
该方法配置文件路径到其中记录的音频将被存储 |
stop() |
该方法停止记录处理 |
release() |
录音结束后,释放 |
大致流程:
效果:
布局文件:
清单文件: 添加权限
java代码 重要的两个类 录制MediaRecoder和播放类MediaPlayer 注意:android 6.0 要动态申请权限
了解更多权限请看:动态获取权限的封装
//长按下按钮,开始录制(文本会变成"请大声说话")
//松开按钮,停止录制(文本复原),播放刚刚录制的声音
public class MainActivity extends AppCompatActivity {
private Button mRecordBtn;
private MediaRecorder mRecord;//录音
private MediaPlayer mPlayer;//播放声音
private File voiceFile;
private int REQ_CODE = 1001;
private volatile boolean flag = false; //权限标识符
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecordBtn = findViewById(R.id.id_btn);
initEvents();
}
private void initEvents() {
//长按按键的监听事件
mRecordBtn.setOnLongClickListener(new View.OnLongClickListener() {
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public boolean onLongClick(View v) {
if (Build.VERSION.SDK_INT >= 23)
requestPermission();
else {
flag = true;
}
//有权限才能操作
if (flag) {
try {
//创建录音对象
mRecord = new MediaRecorder();
//设置声音来源 MIC 系统麦克风
mRecord.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置声音格式 MPEG_4 音频、视频的标准格式
mRecord.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
//设置声音的编码格式
mRecord.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
//准备临时文件用于承载录制的声音
voiceFile = File.createTempFile("myvoice", "MP3");
//将录制好的声音输出到创建好的临时文件中
mRecord.setOutputFile(voiceFile.getAbsoluteFile());
//准备录音
mRecord.prepare();
//开始录制
mRecord.start();
//更改文本
mRecordBtn.setText("录音中...");
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
});
//手向上抬起的按键
mRecordBtn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(final View v, MotionEvent event) {
if (flag) {
if (event.getAction() == MotionEvent.ACTION_UP) {
//停止录制
mRecord.stop();
//释放资源
mRecord.release();
//更改文本
mRecordBtn.setText("点击录音");
try {
//播放录音
mPlayer = new MediaPlayer();
//设置播放源
mPlayer.setDataSource(voiceFile.getAbsolutePath());
//准备
mPlayer.prepare();
//开始
mPlayer.start();
//播放结束后的监听
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mp.stop();
mp.release();
//删掉录音的文件
voiceFile.delete();
//重置
flag = false;
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
});
}
/**
* android 6.0之后动态申请权限
*/
private void requestPermission() {
//如果权限没有被申请
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, REQ_CODE);
} else {
//如果已经申请了
flag = true;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQ_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//权限申请成功
flag = true;
} else {
//权限申请失败
Toast.makeText(this, "需要打开录音权限", Toast.LENGTH_SHORT).show();
}
}
}
}
布局文件省略了
注意要在清单文件中添加录音权限
Msg.java 消息封装类
package com.demo.voicerecorddemo;
import java.io.File;
public class Msg {
//内容、涉及到 语音、发送/接受的标志
private String txt;
private File f;
private int flag; //1:本人 -1:对方
public Msg(String txt, File f, int flag) {
this.txt = txt;
this.f = f;
this.flag = flag;
}
public String getTxt() {
return txt;
}
public void setTxt(String txt) {
this.txt = txt;
}
public File getF() {
return f;
}
public void setF(File f) {
this.f = f;
}
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
this.flag = flag;
}
}
适配器类 ChattingAdapter.java
根据不同的标识来显示不同的布局
//适配器:处理聊天记录样式
public class ChattingAdapter extends BaseAdapter {
private List data;
private Context ctx;
public ChattingAdapter(List data,Context ctx) {
this.data = data;
this.ctx = ctx;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
Msg g = data.get(i);
// if(view == null){ }
if(g.getFlag() == 1) {
view = LayoutInflater.from(ctx).inflate(R.layout.to_msg,null);
}else{
view = LayoutInflater.from(ctx).inflate(R.layout.from_msg,null);
}
TextView msg = (TextView) view.findViewById(R.id.msg);
msg.setText(g.getTxt());
return view;
}
}
注意聊天类: ChattingActivity.java
声音的录制 与播放 存储逻辑 和权限申请
public class ChattingActivity extends AppCompatActivity {
private Button voiceBtn;
private ListView msgList;
private ChattingAdapter adapter;
private List datas = new ArrayList<>();
private MediaRecorder recorder;
private List voices = new ArrayList<>();
private int REQ_CODE = 1001;
private volatile boolean flag = false; //权限标识符
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chatting);
initView();
}
private void initView() {
voiceBtn = findViewById(R.id.vice_btn);
msgList = findViewById(R.id.msg_list);
adapter = new ChattingAdapter(datas, this);
msgList.setAdapter(adapter);
//长按按钮,开始录音,松开按钮,录音结束
//发送语音的效果
//未来在点击了聊天气泡后才播放对应的语音
voiceBtn.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
//android 6.0 之后才需要动态申请权限
if (Build.VERSION.SDK_INT >= 23)
requestPermission();
else {
flag = true;
}
if (flag) {
voiceBtn.setText("请开始说话...");
//录音
//一系列特性的设置
try {
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
//指定语音文件
File tempFile = File.createTempFile("temp", "mp3");
recorder.setOutputFile(tempFile.getAbsolutePath());
//保存语音文件
voices.add(tempFile);
//开始录音
recorder.prepare();
recorder.start();
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
});
voiceBtn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (flag) {//如果没授予权限
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
voiceBtn.setText("按住说话");
//停止录音
recorder.stop();
recorder.release();
flag = false;
//更新聊天界面
datas.add(new Msg(" ", voices.get(voices.size() - 1), 1));
datas.add(new Msg("哈哈哈哈", null, -1)); //伪造一条跟随着的收到的信息
adapter.notifyDataSetChanged();
//让listView一直滚动到底部
msgList.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
}
} else {
if (ContextCompat.checkSelfPermission(ChattingActivity.this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED)
Toast.makeText(ChattingActivity.this, "需要打开录音权限", Toast.LENGTH_SHORT).show();
else {
Toast.makeText(ChattingActivity.this, "长按说话", Toast.LENGTH_SHORT).show();
}
}
return false;
}
});
msgList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView adapterView, View view, int i, long l) {
Msg m = datas.get(i);
try {
if (m.getFlag() == 1) {
final MediaPlayer player = new MediaPlayer();
player.setDataSource(m.getF().getAbsolutePath());
player.prepare();
player.start();
//录音播放完毕
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
player.stop();
player.release();
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* 销毁后删除之前保留的录音
*/
@Override
protected void onDestroy() {
super.onDestroy();
for (int i = 0; i < voices.size(); i++) {
voices.get(i).delete();
}
}
/**
* android 6.0之后动态申请权限
*/
private void requestPermission() {
//如果权限没有被申请
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, REQ_CODE);
} else {
//如果已经申请了
flag = true;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQ_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//权限申请成功
flag = true;
} else {
//权限申请失败
Toast.makeText(this, "需要打开录音权限", Toast.LENGTH_SHORT).show();
}
}
}
}
参考案例:
Android使用MediaRecorder和Camera实现视频录制及播放功能整理
Android多媒体录制--MediaRecorder视频录制
Android (系统+自定义)短视频录制(含暂停继续录制功能) 总结
1. 页面展示两个按钮,分别是开始和停止录制,SurfaceView 位置来展示视频,video_layout.xml 如下:
2. VideoActivity.java 中为按钮绑定监听器,为 SurfaceView 准备回调接口等初始化工作
/**
* 为按钮绑定监听器
* 为SurfaceView 准备回调等初始化操作
*/
public class VideoActivity extends AppCompatActivity implements SurfaceHolder.Callback {
private Button mBtnStart;//开始录制按钮
private Button mBtnStop;//停止录制按钮
private SurfaceView mSurfaceView;//显示视频控件
private SurfaceHolder mHolder;
private SurfaceHolder Holder;
private MediaRecorder mRecorder;//录制视频的类
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//去掉标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
//设置全屏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
//设置横屏显示
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setContentView(R.layout.video_layout);
initViews();
initEvents();
}
/**
* 初始化视图
*/
private void initViews() {
mBtnStart = findViewById(R.id.btn_start);
mBtnStop = findViewById(R.id.btn_stop);
mSurfaceView = findViewById(R.id.id_surfaceView);
}
/**
* 初始化事件
*/
private void initEvents() {
mBtnStart.setOnClickListener(new VideoListener());
mBtnStop.setOnClickListener(new VideoListener());
mHolder = mSurfaceView.getHolder();//取得holder
Log.e("TAG", "此时的mHolder是否为空" + mHolder);
mHolder.addCallback(this);//添加surfaceView的回调
// 设置Surface不需要维护自己的缓冲区
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
/**
* 按钮点击事件
*/
private class VideoListener implements View.OnClickListener {
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onClick(View v) {
if (v == mBtnStart) {
startVideo();
} else {
stopVideo();
}
}
}
}
3. VideoActivity 中 MediaRecorder 录制与停止的工作
/**
* 开始录制视频
*/
private void startVideo() {
//创建MediaRecorder对象
mRecorder = new MediaRecorder();
Camera camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
camera.unlock();
mRecorder.setCamera(camera);
//设置视频录制源为相机
mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
//声音来源
mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
//设置录制完成后视频的封装格式为THREE_GPP 3gp,MPEG_4 为mp4
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
//设置录制视频编码为h263 h264
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
//设置视频录制的分辨率。必须放在设置 编码和格式的后面
mRecorder.setVideoSize(1920, 1440);
//设置录制视频的帧率,必须放在设置编码和格式的后面
mRecorder.setVideoFrameRate(50);
//设置编码率
mRecorder.setVideoEncodingBitRate(3 * 1024 * 1024);
mRecorder.setOrientationHint(90);
//设置记录会话的最大持续时间(毫秒)
mRecorder.setMaxDuration(30 * 1000);
File file = new File(Environment.getExternalStorageDirectory().getPath() + "/video/test.mp4");
if (file.exists()) {
file.delete();
}
//设置视频文件的输出路径
mRecorder.setOutputFile(file.getAbsolutePath());
//让录制的视频放在surfaceView上预览
mRecorder.setPreviewDisplay(Holder.getSurface());
try {
//准备录制
mRecorder.prepare();
//开始录制
mRecorder.start();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 停止录制视频
*/
private void stopVideo() {
if (mRecorder != null) {
try {
mRecorder.stop();
mRecorder.release();//释放资源
mRecorder = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
4. VideoActivity 中为视频展示相关的 SurfaceHolder.Callback
@Override
public void surfaceCreated(SurfaceHolder holder) {
//将holder ,这个holder为开始在initView里面取得的holder,将它赋给Holder
Holder = holder;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//将holder ,这个holder为开始在initView里面取得的holder,将它赋给Holder
Holder = holder;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mSurfaceView = null;
mHolder = null;
mRecorder = null;
}
5. 最后别忘记申请权限
android 6.0之后还需要动态申请权限:动态获取权限的封装