Android完美实现录音笔记功能
可能是神马笔记最长的一个开发版本了。
6月10日开始进行技术准备,6月26日才提交新版本测试。
前后用了17天的时间,最终的实现效果非常棒,可能是安卓平台最好用的录音笔记功能。
一、目标
实现神马笔记的录音笔记功能。
二、准备工作回顾
经过了10个开发过程,终于可以开始整合最终功能了。
序号 | 类别 | 开发过程 | 描述 |
---|---|---|---|
1 | 录音编辑器 | Android实现录音功能汇总 | 决定录音的实现方式 |
2 | Android低仿iOS Messages录音波形效果 | 实现录音波形 | |
3 | Android高仿iOS Messages声音播放波形效果 | 实现播放波形 | |
4 | Android高仿iOS Messages录音操作按钮 | 实现录音按钮 | |
5 | Android使用PopupWindow高仿iOS Messages录音弹出界面 | 实现录音界面 | |
6 | Android完美实现录音编辑器 | 最终编辑器 | |
7 | 音频播放器 | Android高仿iOS圆环进度条 | 播放进度条 |
8 | Android使用MediaPlayer播放音频 | 最终播放器 | |
9 | 音频控制器 | Android使用AudioManager切换到听筒模式 | 控制音频输出 |
10 | 便捷操作 | Android模仿iOS Messages拿起手机靠近耳朵自动录音 | 实现快速录音 |
三、开发过程
1. 增加录音功能
截图 | 说明 |
---|---|
![]() |
右下角的发送按钮,调整为录音按钮。 长按录音按钮启动录音功能。 |
![]() |
点击录音按钮,显示操作提示。 |
![]() |
长按录音按钮,启动录音功能。 |
2. 请求录音权限
录音功能是敏感功能,因此使用之前必须请求用户授权。使用RxPermissions
可以很容易实现这个功能。
void requestTape() {
this.tapeHelper.stop();
this.tapeHelper.resetSpeaker();
PermissionHelper helper = new PermissionHelper(getActivity());
helper.setPermission(Manifest.permission.RECORD_AUDIO);
final boolean isGranted = helper.isGranted();
PermissionHelper.OnPermissionListener consumer = (h) -> {
bottomBar.chatBar.setVisibility(View.INVISIBLE);
popupTape.show(isGranted);
};
String requestMsg = "请允许录制音频,以发送语音。";
String deniedMsg = "请在「权限管理」,设置允许「麦克风」,以发送语音。";
helper.setRequestMessage(requestMsg);
helper.setDeniedMessage(deniedMsg);
helper.setOnPermissionListener(consumer);
helper.request();
}
3. 整合录音编辑器
用户授权通过后,即可开始进行录音。
之前的开发将录音编辑器独立为单独的功能模块,直接使用即可。
需要注意的是dispatchTouchEvent
的处理。
需要手动处理dispatchTouchEvent
才能实现弹出后,用户可以继续操作。
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
FragmentManager fm = getSupportFragmentManager();
Fragment f = fm.findFragmentByTag(CONTENT);
if (f != null && f instanceof OnDispatchKeyEventListener) {
OnDispatchTouchEventListener listener = (OnDispatchTouchEventListener)f;
boolean result = listener.dispatchTouchEvent(ev);
if (result) {
return true;
}
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (popupTape != null && popupTape.dispatchTouchEvent(event)) {
return true;
}
return false;
}
4. 定义录音数据结构
录音数据结构包含3个层次。
序号 | 类定义 | 说明 |
---|---|---|
1 | TapeEntry | 存储数据 |
2 | TapeEntity | 逻辑数据 |
3 | TapeItem | 交互数据 |
以及与TapeItem
对应的ViewHolder
类。
TapeItem
public class TapeItem extends ChatItem<TapeEntity> {
Waveform waveform;
public TapeItem(ChatProvider parent, TapeEntity entity) {
super(parent, entity, TYPE_TAPE);
}
@Override
protected int getTypeStyle() {
return STYLE_SINGLE;
}
public int getDuration() {
return entity.getDuration();
}
public File getFile() {
return entity.getFile();
}
public Waveform getWaveform() {
if (waveform == null) {
this.waveform = Waveform.read(entity.getWaveform());
}
return waveform;
}
public void setWaveform(Waveform waveform) {
this.waveform = waveform;
}
public TapeHelper getTapeHelper() {
return parent.getTapeHelper();
}
public AudioPlayer getAudioPlayer() {
return getTapeHelper().getAudioPlayer();
}
public boolean isActive() {
TapeHelper helper = this.getTapeHelper();
boolean result = (helper.getActiveItem() == this);
return result;
}
public void activate() {
TapeHelper helper = this.getTapeHelper();
helper.setActiveItem(this);
}
public