运行有问题或需要源码请点赞关注收藏后评论区留言~~~
一、使用录音机录制音频
手机有自带的系统相机,也有自带的系统录音机,只要在调用startActivityForResult之前指定该动作,就会自动跳转到系统的录音机界面 效果如下
当然这里最好连接真机测试 模拟机好像没有录音机 总之调试有点麻烦
代码如下
Java类
package com.example.chapter13; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import com.example.chapter13.util.DateUtil; import com.example.chapter13.util.FileUtil; public class AudioRecordActivity extends AppCompatActivity implements View.OnClickListener { private final static String TAG = "AudioRecordActivity"; private int RECORDER_CODE = 1; // 录制操作的请求码 private TextView tv_audio; private ImageView iv_audio; // 该图标充当播放按钮 private Uri mAudioUri; // 音频文件的uri路径 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_audio_record); tv_audio = findViewById(R.id.tv_audio); iv_audio = findViewById(R.id.iv_audio); findViewById(R.id.btn_recorder).setOnClickListener(this); findViewById(R.id.iv_audio).setOnClickListener(this); } @Override public void onClick(View v) { if (v.getId() == R.id.btn_recorder) { // 下面打开系统自带的录音机 Intent intent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION); registerForActivityResult(intent, RECORDER_CODE); // 跳到录音机页面 } else if (v.getId() == R.id.iv_audio) { // 下面打开系统自带的收音机 Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(mAudioUri, "audio/*"); // 类型为音频 startActivity(intent); // 跳到收音机页面 } } private void registerForActivityResult(Intent intent, int recorder_code) { } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); if (resultCode==RESULT_OK && requestCode==RECORDER_CODE){ mAudioUri = intent.getData(); // 获得录制好的音频uri String filePath = String.format("%s/%s.mp3", getExternalFilesDir(Environment.DIRECTORY_MUSIC), "audio_"+ DateUtil.getNowDateTime()); FileUtil.saveFileFromUri(this, mAudioUri, filePath); // 保存为临时文件 tv_audio.setText("录制完成的音频地址为:"+mAudioUri.toString()); iv_audio.setVisibility(View.VISIBLE); } } }
XML文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:id="@+id/btn_recorder" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="打开录音机" android:textColor="@color/black" android:textSize="17sp" /> <TextView android:id="@+id/tv_audio" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="5dp" android:textColor="@color/black" android:textSize="17sp" /> <ImageView android:id="@+id/iv_audio" android:layout_width="match_parent" android:layout_height="50dp" android:scaleType="fitCenter" android:src="@drawable/play_audio" android:visibility="gone" /> </LinearLayout>
二、利用MediaPlayer播放音频
尽管让App跳到收音机界面就能播放音频,但是通常App都不希望用户离开自身页面,何况播音本来就是一个小功能,完全可以一边播放音频一边操作界面,若想在App内部自己播音,便用到了媒体播放器MediaPlayer,并且要找到音频文件的路径。
MediaPlayer常用方法如下
1:reset 重置播放器
2:prepare 准备播放
3:start 开始播放
4:pause 暂停播放
5:stop 停止播放
6:create 创建uri指定的播放器
7:setDataSource 设置播放器数据来源的文件路径
应用场景主要有以下两个1:播放指定音频文件
2:退出页面时准备释放媒体资源
代码如下
Java类
package com.example.chapter13; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import android.database.Cursor; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.provider.MediaStore; import android.util.Log; import android.view.View; import com.example.chapter13.adapter.AudioRecyclerAdapter; import com.example.chapter13.bean.MediaInfo; import com.example.chapter13.util.FileUtil; import com.example.chapter13.widget.RecyclerExtras; import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; public class AudioPlayActivity extends AppCompatActivity implements RecyclerExtras.OnItemClickListener { private final static String TAG = "AudioPlayActivity"; private RecyclerView rv_audio; // 音频列表的循环视图 private List<MediaInfo> mAudioList = new ArrayList<MediaInfo>(); // 音频列表 private Uri mAudioUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; // 音频库的Uri private String[] mAudioColumn = new String[]{ // 媒体库的字段名称数组 MediaStore.Audio.Media._ID, // 编号 MediaStore.Audio.Media.TITLE, // 标题 MediaStore.Audio.Media.DURATION, // 播放时长 MediaStore.Audio.Media.SIZE, // 文件大小 MediaStore.Audio.Media.DATA}; // 文件路径 private AudioRecyclerAdapter mAdapter; // 音频列表的适配器 private MediaPlayer mMediaPlayer = new MediaPlayer(); // 媒体播放器 private Timer mTimer = new Timer(); // 计时器 private int mLastPosition = -1; // 上次播放的音频序号 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_audio_play); rv_audio = findViewById(R.id.rv_audio); loadAudioList(); // 加载音频列表 showAudioList(); // 显示音频列表 } // 加载音频列表 private void loadAudioList() { mAudioList.clear(); // 清空音频列表 // 通过内容解析器查询音频库,并返回结果集的游标。记录结果按照修改时间降序返回 Cursor cursor = getContentResolver().query(mAudioUri, mAudioColumn, null, null, "date_modified desc"); if (cursor != null) { // 下面遍历结果集,并逐个添加到音频列表。简单起见只挑选前十个音频 for (int i=0; i<10 && cursor.moveToNext(); i++) { MediaInfo audio = new MediaInfo(); // 创建一个音频信息对象 audio.setId(cursor.getLong(0)); // 设置音频编号 audio.setTitle(cursor.getString(1)); // 设置音频标题 audio.setDuration(cursor.getInt(2)); // 设置音频时长 audio.setSize(cursor.getLong(3)); // 设置音频大小 audio.setPath(cursor.getString(4)); // 设置音频路径 Log.d(TAG, audio.getTitle() + " " + audio.getDuration() + " " + audio.getSize() + " " + audio.getPath()); if (!FileUtil.checkFileUri(this, audio.getPath())) { i--; continue; } mAudioList.add(audio); // 添加至音频列表 } cursor.close(); // 关闭数据库游标 } } // 显示音频列表 private void showAudioList() { // 创建一个水平方向的线性布局管理器 LinearLayoutManager manager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false); rv_audio.setLayoutManager(manager); // 设置循环视图的布局管理器 mAdapter = new AudioRecyclerAdapter(this, mAudioList); // 创建音频列表的线性适配器 mAdapter.setOnItemClickListener(this); // 设置线性列表的点击监听器 rv_audio.setAdapter(mAdapter); // 设置循环视图的列表适配器 } @Override protected void onDestroy() { super.onDestroy(); mTimer.cancel(); // 取消计时器 if (mMediaPlayer.isPlaying()) { // 是否正在播放 mMediaPlayer.stop(); // 结束播放 } mMediaPlayer.release(); // 释放媒体播放器 } @Override public void onItemClick(View view, final int position) { if (mLastPosition!=-1 && mLastPosition!=position) { MediaInfo last_audio = mAudioList.get(mLastPosition); last_audio.setProgress(-1); // 当前进度设为-1表示没在播放 mAudioList.set(mLastPosition, last_audio); mAdapter.notifyItemChanged(mLastPosition); // 刷新此处的列表项 } mLastPosition = position; final MediaInfo audio = mAudioList.get(position); Log.d(TAG, "onItemClick position="+position+",audio.getPath()="+audio.getPath()); mTimer.cancel(); // 取消计时器 mMediaPlayer.reset(); // 重置媒体播放器 // mMediaPlayer.setVolume(0.5f, 0.5f); // 设置音量,可选 mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 设置音频流的类型为音乐 try { mMediaPlayer.setDataSource(audio.getPath()); // 设置媒体数据的文件路径 mMediaPlayer.prepare(); // 媒体播放器准备就绪 mMediaPlayer.start(); // 媒体播放器开始播放 } catch (Exception e) { e.printStackTrace(); } mTimer = new Timer(); // 创建一个计时器 mTimer.schedule(new TimerTask() { @Override public void run() { audio.setProgress(mMediaPlayer.getCurrentPosition()); // 设置进度条的当前进度 mAudioList.set(position, audio); // 界面刷新操作需要在主线程执行,故而向处理器发送消息,由处理器在主线程更新界面 mHandler.sendEmptyMessage(position); Log.d(TAG, "CurrentPosition="+mMediaPlayer.getCurrentPosition()+",position="+position); } }, 0, 1000); // 计时器每隔一秒就更新进度条上的播放进度 } private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); mAdapter.notifyItemChanged(msg.what); // 刷新此处的列表项 } }; }
XML文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="5dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="点击音频列表开始播放" android:textColor="@color/black" android:textSize="17sp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="40dp" android:layout_margin="2dp" android:orientation="horizontal"> <TextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3" android:gravity="left|center" android:text="音频名称" android:textColor="@color/black" android:textSize="15sp" /> <TextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="right|center" android:text="总时长" android:textColor="@color/black" android:textSize="15sp" /> </LinearLayout> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv_audio" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> </LinearLayout>
三、利用MediaRecoder录制音频
Android提供了媒体录制器MediaRecoder,它既能录制音频也能录制视频,它可以在当前页面直接录音,而不必跳到系统自带的录音机界面。常用方法与MediaPlayer基本一致
它的基本应用场景也只有两个1:开始录制媒体文件
2:停止录制媒体文件
其中录制音频的场景需要经历以下步骤
重置录制器-设置媒体文件的路径-准备录制-开始录制
效果如下 可以自行选择编码格式和录制时长
代码如下
Java类
package com.example.chapter13; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.MediaRecorder; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import com.example.chapter13.util.MediaUtil; import java.util.Timer; import java.util.TimerTask; public class MediaRecorderActivity extends AppCompatActivity implements View.OnClickListener, MediaRecorder.OnInfoListener { private static final String TAG = "MediaRecorderActivity"; private Button btn_record; private LinearLayout ll_progress; private ProgressBar pb_record; // 声明一个进度条对象 private TextView tv_progress; private ImageView iv_audio; // 该图标充当播放按钮 private MediaRecorder mMediaRecorder = new MediaRecorder(); // 媒体录制器 private boolean isRecording = false; // 是否正在录制 private int mAudioEncoder; // 音频编码 private int mOutputFormat; // 输出格式 private int mDuration; // 录制时长 private String mRecordFilePath; // 录制文件的保存路径 private Timer mTimer = new Timer(); // 计时器 private int mTimeCount; // 时间计数 private MediaPlayer mMediaPlayer = new MediaPlayer(); // 媒体播放器 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_media_recorder); btn_record = findViewById(R.id.btn_record); ll_progress = findViewById(R.id.ll_progress); pb_record = findViewById(R.id.pb_record); tv_progress = findViewById(R.id.tv_progress); iv_audio = findViewById(R.id.iv_audio); btn_record.setOnClickListener(this); iv_audio.setOnClickListener(this); initEncoderSpinner(); // 初始化音频编码的下拉框 initFormatSpinner(); // 初始化输出格式的下拉框 initDurationSpinner(); // 初始化录制时长的下拉框 } // 初始化音频编码的下拉框 private void initEncoderSpinner() { ArrayAdapter<String> encoderAdapter = new ArrayAdapter<String>(this, R.layout.item_select, encoderDescArray); Spinner sp_encoder = findViewById(R.id.sp_encoder); sp_encoder.setPrompt("请选择音频编码"); // 设置下拉框的标题 sp_encoder.setAdapter(encoderAdapter); // 设置下拉框的数组适配器 // 给下拉框设置选择监听器,一旦用户选中某一项,就触发监听器的onItemSelected方法 sp_encoder.setOnItemSelectedListener(new EncoderSelectedListener()); sp_encoder.setSelection(0); // 设置下拉框默认显示第一项 } private String[] encoderDescArray = { "默认编码", "窄带编码", "宽带编码", "低复杂度的高级编码", "高效率的高级编码", "增强型低延时的高级编码" }; private int[] encoderArray = { MediaRecorder.AudioEncoder.DEFAULT, MediaRecorder.AudioEncoder.AMR_NB, MediaRecorder.AudioEncoder.AMR_WB, MediaRecorder.AudioEncoder.AAC, MediaRecorder.AudioEncoder.HE_AAC, MediaRecorder.AudioEncoder.AAC_ELD }; class EncoderSelectedListener implements AdapterView.OnItemSelectedListener { public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) { mAudioEncoder = encoderArray[arg2]; } public void onNothingSelected(AdapterView<?> arg0) {} } // 初始化输出格式的下拉框 private void initFormatSpinner() { ArrayAdapter<String> formatAdapter = new ArrayAdapter<String>(this, R.layout.item_select, formatDescArray); Spinner sp_format = findViewById(R.id.sp_format); sp_format.setPrompt("请选择输出格式"); // 设置下拉框的标题 sp_format.setAdapter(formatAdapter); // 设置下拉框的数组适配器 sp_format.setSelection(0); // 设置下拉框默认显示第一项 // 给下拉框设置选择监听器,一旦用户选中某一项,就触发监听器的onItemSelected方法 sp_format.setOnItemSelectedListener(new FormatSelectedListener()); } private String[] formatDescArray = { "默认格式", "窄带格式", "宽带格式", "高级的音频传输流格式" }; private int[] formatArray = { MediaRecorder.OutputFormat.DEFAULT, MediaRecorder.OutputFormat.AMR_NB, MediaRecorder.OutputFormat.AMR_WB, MediaRecorder.OutputFormat.AAC_ADTS }; class FormatSelectedListener implements AdapterView.OnItemSelectedListener { public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) { mOutputFormat = formatArray[arg2]; } public void onNothingSelected(AdapterView<?> arg0) {} } // 初始化录制时长的下拉框 private void initDurationSpinner() { ArrayAdapter<String> durationAdapter = new ArrayAdapter<String>(this, R.layout.item_select, durationDescArray); Spinner sp_duration = findViewById(R.id.sp_duration); sp_duration.setPrompt("请选择录制时长"); // 设置下拉框的标题 sp_duration.setAdapter(durationAdapter); // 设置下拉框的数组适配器 sp_duration.setSelection(0); // 设置下拉框默认显示第一项 // 给下拉框设置选择监听器,一旦用户选中某一项,就触发监听器的onItemSelected方法 sp_duration.setOnItemSelectedListener(new DurationSelectedListener()); } private String[] durationDescArray = {"5秒", "10秒", "20秒", "30秒", "60秒"}; private int[] durationArray = {5, 10, 20, 30, 60}; class DurationSelectedListener implements AdapterView.OnItemSelectedListener { public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) { mDuration = durationArray[arg2]; } public void onNothingSelected(AdapterView<?> arg0) {} } @Override public void onClick(View v) { if (v.getId() == R.id.btn_record) { if (!isRecording) { // 未在录音 startRecord(); // 开始录音 } else { // 正在录音 stopRecord(); // 停止录音 } } else if (v.getId() == R.id.iv_audio) { mMediaPlayer.reset(); // 重置媒体播放器 mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 设置音频流的类型为音乐 try { mMediaPlayer.setDataSource(mRecordFilePath); // 设置媒体数据的文件路径 mMediaPlayer.prepare(); // 媒体播放器准备就绪 mMediaPlayer.start(); // 媒体播放器开始播放 } catch (Exception e) { e.printStackTrace(); } } } // 开始录音 private void startRecord() { Log.d(TAG, "startRecord mAudioEncoder="+mAudioEncoder+", mOutputFormat="+mOutputFormat+", mDuration="+mDuration); ll_progress.setVisibility(View.VISIBLE); isRecording = !isRecording; btn_record.setText("停止录制"); pb_record.setMax(mDuration); // 设置进度条的最大值 mTimeCount = 0; // 时间计数清零 mTimer = new Timer(); // 创建一个计时器 mTimer.schedule(new TimerTask() { @Override public void run() { pb_record.setProgress(mTimeCount); // 设置进度条的当前进度 tv_progress.setText(MediaUtil.formatDuration(mTimeCount*1000)); mTimeCount++; } }, 0, 1000); // 计时器每隔一秒就更新进度条上的录制进度 // 获取本次录制的媒体文件路径 mRecordFilePath = MediaUtil.getRecordFilePath(this, "RecordAudio", ".amr"); // 下面是媒体录制器的处理代码 mMediaRecorder.reset(); // 重置媒体录制器 mMediaRecorder.setOnInfoListener(this); // 设置媒体录制器的信息监听器 mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置音频源为麦克风 mMediaRecorder.setOutputFormat(mOutputFormat); // 设置媒体的输出格式。该方法要先于setAudioEncoder调用 mMediaRecorder.setAudioEncoder(mAudioEncoder); // 设置媒体的音频编码器 // mMediaRecorder.setAudioSamplingRate(8); // 设置媒体的音频采样率。可选 // mMediaRecorder.setAudioChannels(2); // 设置媒体的音频声道数。可选 // mMediaRecorder.setAudioEncodingBitRate(1024); // 设置音频每秒录制的字节数。可选 mMediaRecorder.setMaxDuration(mDuration * 1000); // 设置媒体的最大录制时长 // mMediaRecorder.setMaxFileSize(1024*1024*10); // 设置媒体的最大文件大小 // setMaxFileSize与setMaxDuration设置其一即可 mMediaRecorder.setOutputFile(mRecordFilePath); // 设置媒体文件的保存路径 try { mMediaRecorder.prepare(); // 媒体录制器准备就绪 mMediaRecorder.start(); // 媒体录制器开始录制 } catch (Exception e) { e.printStackTrace(); } } // 停止录音 private void stopRecord() { isRecording = !isRecording; btn_record.setText("开始录制"); mTimer.cancel(); // 取消定时器 mMediaRecorder.stop(); // 媒体录制器停止录制 } @Override public void onInfo(MediaRecorder mr, int what, int extra) { // 录制达到最大时长,或者达到文件大小限制,都停止录制 if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED || what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) { stopRecord(); // 停止录音 iv_audio.setVisibility(View.VISIBLE); Toast.makeText(this, "已结束录制,音频文件路径为"+mRecordFilePath, Toast.LENGTH_LONG).show(); } } @Override protected void onStop() { super.onStop(); if (!TextUtils.isEmpty(mRecordFilePath) && isRecording) { stopRecord(); // 停止录音 } if (mMediaPlayer.isPlaying()) { // 如果正在播放 mMediaPlayer.stop(); // 停止播放 } } @Override protected void onDestroy() { super.onDestroy(); mMediaRecorder.release(); // 释放媒体录制器 mMediaPlayer.release(); // 释放媒体播放器 } }
XML文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="40dp" android:paddingLeft="5dp"> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:text="音频编码:" android:textColor="@color/black" android:textSize="17sp" /> <Spinner android:id="@+id/sp_encoder" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="left|center" android:spinnerMode="dialog" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="40dp" android:paddingLeft="5dp"> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:text="输出格式:" android:textColor="@color/black" android:textSize="17sp" /> <Spinner android:id="@+id/sp_format" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="left|center" android:spinnerMode="dialog" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="40dp" android:paddingLeft="5dp"> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:text="录制时长:" android:textColor="@color/black" android:textSize="17sp" /> <Spinner android:id="@+id/sp_duration" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="left|center" android:spinnerMode="dialog" /> </LinearLayout> <Button android:id="@+id/btn_record" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="开始录音" android:textColor="@color/black" android:textSize="17sp" /> <LinearLayout android:id="@+id/ll_progress" android:layout_width="match_parent" android:layout_height="30dp" android:paddingLeft="5dp" android:paddingRight="5dp" android:orientation="horizontal" android:visibility="gone"> <ProgressBar android:id="@+id/pb_record" style="?android:attr/progressBarStyleHorizontal" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="4" /> <TextView android:id="@+id/tv_progress" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="right|center" android:textColor="@color/black" android:textSize="15sp" /> </LinearLayout> <ImageView android:id="@+id/iv_audio" android:layout_width="match_parent" android:layout_height="50dp" android:scaleType="fitCenter" android:src="@drawable/play_audio" android:visibility="gone" /> </LinearLayout>
创作不易 觉得有帮助请点赞关注收藏~~~