@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_record_audio, null);
mFabRecord.setOnClickListener(new View.OnClickListener() {@Override
br/>@Override
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(getActivity()
, new String[
]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, 1);
}else {
onRecord(mStartRecording);
mStartRecording = !mStartRecording;
}
}
});
builder.setView(view);
return builder.create();
}
private void onRecord(boolean start) {
Intent intent = new Intent(getActivity(), RecordingService.class);
if (start) {
File folder = new File(Environment.getExternalStorageDirectory() + "/SoundRecorder");
if (!folder.exists()) {
folder.mkdir();
}
mChronometerTime.setBase(SystemClock.elapsedRealtime());
mChronometerTime.start();
getActivity().startService(intent);
} else {
mChronometerTime.stop();
timeWhenPaused = 0;
getActivity().stopService(intent);
}
}
}
可以看到在 RecordAudioDialogFragment 有一个 newInstance(int maxTime) 的静态方法供外部调用,如果想设置录音的最大时长,直接传参数进去就行了。
好的,敲黑板,重点来了,其实这个对话框的重点部分就是在 onCreateDialog()中,我们先加载了我们自定义的对话框的布局,当点击录音的按钮的时候,先进行相关权限的申请,这里有个巨坑,录音权限 android.permission.RECORD_AUDIO 在不久前还是普通权限的,不知道什么时候突然变成了危险权限,需要我们进行申请,Google 真是会玩。
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_record_audio, null);
mFabRecord.setOnClickListener(new View.OnClickListener() {@Override
br/>@Override
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(getActivity()
, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, 1);
}else {
onRecord(mStartRecording);
mStartRecording = !mStartRecording;
}
}
});
builder.setView(view);
return builder.create();
}
申请好权限之后便会调用 onRecord() 这个方法,然后将 boolean mStartRecording 进行反转,这样就不用写难看的 if else 了,直接改变 mStartRecording 的值,然后在onRecord() 里面进行处理
接下来看下 onRecord 干了什么
private void onRecord(boolean start) {
Intent intent = new Intent(getActivity(), RecordingService.class);
if (mStartRecording) {
File folder = new File(Environment.getExternalStorageDirectory() + "/SoundRecorder");
if (!folder.exists()) {
folder.mkdir();
}
mChronometerTime.setBase(SystemClock.elapsedRealtime());
mChronometerTime.start();
getActivity().startService(intent);
} else {
mChronometerTime.stop();
timeWhenPaused = 0;
getActivity().stopService(intent);
}
}
好吧,其实并没有干了什么大事,只是创建了保存录音文件的文件夹,然后根据mStartRecording 的值进行 RecordingService 的启动和关闭罢了。在启动时还顺便开始了 mChronometer 的计时显示,这是一个 Android 原生的显示计时的一个控件。
###三、播放录音的 PlaybackDialogFragment
其实,如果只是录音这一块的话,写个 MediaPlayer 就可以了,然而还要写播放的时间进度,以及显示一个稍微好看点的进度条,我能怎样,我也很烦啊。
外部调用这个对话框的时候,只需要传入一个包含录音文件信息的 RecordingItem,因为包含的信息比较多,所以最好将 RecordingItem 进行序列化。
public static PlaybackDialogFragment newInstance(RecordingItem item) {
PlaybackDialogFragment fragment = new PlaybackDialogFragment();
Bundle bundle = new Bundle();
bundle.putParcelable(ARG_ITEM, item);
fragment.setArguments(b);
return fragment;
}
好,重点又来了,来看看 onCreateDialog() 方法,在加载了布局之后,给 mSeekBar 设置监听,mSeekBar 是一个显示进度条的控件,当开始播放录音时候,将录音文件的时长,设置进 mSeekBar 里面,播放录音的同时,运行 mSeekBar,通过监听 mSeekBar 的进度,刷新显示的播放进度。
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_media_playback, null);
mTvFileLength.setText(String.valueOf(mFileLength));
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Override
br/>@Override
if(mMediaPlayer != null && fromUser) {
mMediaPlayer.seekTo(progress);
mHandler.removeCallbacks(mRunnable);
long minutes = TimeUnit.MILLISECONDS.toMinutes(mMediaPlayer.getCurrentPosition());
long seconds = TimeUnit.MILLISECONDS.toSeconds(mMediaPlayer.getCurrentPosition())
- TimeUnit.MINUTES.toSeconds(minutes);
mCurrentProgressTextView.setText(String.format("%02d:%02d", minutes,seconds));
updateSeekBar();
} else if (mMediaPlayer == null && fromUser) {
prepareMediaPlayerFromPoint(progress);
updateSeekBar();
}
}
});
mPlayButton.setOnClickListener(new View.OnClickListener() {@Override
br/>@Override
onPlay(isPlaying);
isPlaying = !isPlaying;
}
});
mTvFileLength.setText(String.format("%02d:%02d", minutes,seconds));
builder.setView(view);
return builder.create();
}
当点击播放录音的按钮之后,会调用 onPlay() 方法,然后根据 isPlaying(标识当前是否播放录音)的值,来调用不同的方法
private void onPlay(boolean isPlaying){
if (!isPlaying) {
if(mMediaPlayer == null) {
startPlaying(); //start from beginning
}
} else {
pausePlaying();
}
}
我们最关心的,莫过于 startPlaying() 这个方法,这个方法便是来开启播放录音的,我们首先将外部传入的有关的录音信息,设置给 MediaPlayer,然后开始调用mMediaPlayer.start() 进行录音的播放,然后调用 updateSeekbar() 实时更新进度条的内容。当 MediaPlayer 的内容播放完成后,调用 stopPlaying() 方法,关闭mMediaPlayer。
private void startPlaying() {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDataSource(item.getFilePath());
mMediaPlayer.prepare();
mSeekBar.setMax(mMediaPlayer.getDuration());
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {@Override
br/>@Override
mMediaPlayer.start();
}
});
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {@Override
br/>@Override
stopPlaying();
}
});
updateSeekBar();
}
以上便是本文的全部内容,喜欢的可以关注
###四.写在最后
尾声
如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
这里,笔者分享一份从架构哲学的层面来剖析的视频及资料给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。
Android进阶学习资料库
一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!
大厂面试真题
PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
《2019-2021字节跳动Android面试历年真题解析》