一、概述:
1、需要实现的功能:
1)语音的录制
2)按钮状态的切换
3)对话框状态的切换
4)聊天界面的切换
2、所用到的技术:
1)核心技术:根据按钮的onTouchEvent实现down、move、up触发的事件
down时:
```
changeState(state_record)
LongClick->AudioManager.prepare()->end parepared -> DialogManager.showDialog(recording);
```
move时:
```
if(wantCancel(x,y){
DialogManager.showDialog(want_to_cancel);
changeState(state_want_to_cancel)
}else{
DialogManager.showDialog(record);
changeState(state_record)
}
```
up时:
```
if(wantCancel == curState){
AudioManager.cancel();
}else if(state_recode == curState){
AudioManager.release();
callbackToActivity(url,time);
}
```
3、主要的三个类存在的状态:
1)AudioRecorderButton类(录音按钮):
State:STATE_NORMAL【正常状态】、STATE_RECORDERING【正在录音的状态】、STATE_WANT_TO_CALCEL【想要放弃录音的装填】;
(2)AudioDialogManage类(录音过程中的提示对话框):
Style:RECORDERING【正在录音】、WANT_TO_CANCEL【取消提示对话框】、TOO_SHAORT【录音时间过短提醒对话框】;
(3)AudioManage类(控制录音):
prepare()(end prepare-—》callback)【去录音】, cancel()【取消录音】, release()【正常结束】(-—》callbackToActivity), getVoiceLevel()【获得音量大小】;
4、效果图:
5、重点说明:
DialogHelper类里使用了context 是Activity级别的,而Dialog是application级别的,
如果使用了单例模式,在其它Activity里,Dialog就不能释放,就会造成内存泄漏,产生anr异常
二、搭建基本框架:
1、创建按钮:
/**
* @描述 TODO
* @项目名称 App_imooc
* @包名 com.android.imooc.chat
* @类名 RecordButton
* @author chenlin
* @date 2013年6月13日 下午10:42:50
*/
public class RecordButton extends Button {
public RecordButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RecordButton(Context context) {
this(context, null);
}
public RecordButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
}
2、创建布局,把按钮添加到布局里
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#ebebeb"
android:divider="@null"
android:dividerHeight="10dp" >
</ListView>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<!-- minHeight消除主界面上的一些间距 -->
<com.android.imooc.chat.RecordButton
android:id="@+id/recordButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="7dp"
android:layout_marginLeft="50dp"
android:layout_marginRight="50dp"
android:layout_marginTop="6dp"
android:background="@drawable/button_recordnormal"
android:gravity="center"
android:minHeight="0dp"
android:padding="5dp"
android:text="@string/normal"
android:textColor="#727272" >
</com.android.imooc.chat.RecordButton>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#ccc" />
</FrameLayout>
</LinearLayout>
3、创建主页:
/**
* @描述 聊天布局
* @项目名称 App_imooc
* @包名 com.android.imooc.chat
* @类名 ChatActivity
* @author chenlin
* @date 2013年6月13日 下午10:31:59
* @version 1.0
*/
public class ChatActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
}
}
三、按钮的基本功能实现:
1、添加状态
//按住说话状态
private static final int STATE_NORMAL = 0x00;
//正在录音状态
private static final int STATE_RECORDING = 0x01;
//准备取消状态
private static final int STATE_WANT_TO_CANCEL = 0x02;
//当前状态
private int curState = STATE_NORMAL;
//表示已经录音
private boolean isRecording;
2、添加ontouchEvent
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
changeState(STATE_RECORDING);
break;
case MotionEvent.ACTION_MOVE:
if (isRecording) {
//根据坐标判断是否要取消
if (wantToCancel(x, y)) {
changeState(STATE_WANT_TO_CANCEL);
}else {
changeState(STATE_RECORDING);
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (curState == STATE_RECORDING) {
}else if (curState == STATE_WANT_TO_CANCEL) {
}
//恢复状态等信息
reset();
break;
default:
break;
}
return true;
}
private void reset() {
isRecording = false;
}
private boolean wantToCancel(int x, int y) {
// TODO Auto-generated method stub
return false;
}
private void changeState(int stateRecording) {
// TODO Auto-generated method stub
}
3、实现changeState方法,在这个方法里,根据传入的状态,设置按钮的颜色,背景色等
private void changeState(int state) {
if (mCurState != state) {
mCurState = state;
}
switch (state) {
case STATE_NORMAL:
setBackgroundResource(R.drawable.button_recordnormal);
setText(R.string.chat_normal);
break;
case STATE_RECORDING:
setBackgroundResource(R.drawable.button_recording);
setText(R.string.chat_recording);
break;
case STATE_WANT_TO_CANCEL:
setBackgroundResource(R.drawable.button_recording);
setText(R.string.chat_want_to_cancel);
break;
default:
break;
}
}
4、实现判断是否要取消状态,根据手指的移到范围
private boolean wantToCancel(int x, int y) {
//如果手指不在按钮的x轴范围内
if (x < 0 || x > getWidth()) {
return true;
}
//getHeight是按钮的高度
if (y < -DISTANCE_Y_CANCEL || y > getHeight() + DISTANCE_Y_CANCEL) {
return true;
}
return false;
}
四、对话框的实现:
1、从效果图可发现,对话框里有声音图片,背景图片,文字等
所以要定义
public class DialogHelper {
private Dialog mDialog;
private ImageView mIcon;
private ImageView mVoice;
private TextView mLabel;
private Context mContext;
public DialogHelper(Context context){
this.mContext = context;
}
}
2、需要实现的方法:
/**
* 显示
*/
public void showDialog(){
}
/**
* 准备关闭
*/
public void wantToCancel(){
}
/**
* 时间太短提示
*/
public void tooShort(){
}
/**
* 关闭
*/
public void dimiss(){
}
/**
* 更新声音的大小
* @param level
*/
public void updateVoiceLevel(int level){
}
3、实现showDialog方法
1)首先得定义布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/dialog_loading_bg"
android:gravity="center"
android:orientation="vertical"
android:padding="20dp"
tools:context="com.example.weixin_record.MainActivity" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ImageView
android:id="@+id/dialog_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/recorder"
android:visibility="visible" />
<ImageView
android:id="@+id/dialog_voice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/v1"
android:visibility="visible" />
</LinearLayout>
<TextView
android:id="@+id/recorder_dialogtext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="@string/chat_moveUp"
android:textColor="#ffffffff" />
</LinearLayout>
如图:
2)定义样式:
<!-- 设置弹出窗口的属性,frame叠加,isfloat是否浮动,tarnslucent是否半透明,dim是背景是否变暗 -->
<style name="Theme_audioDialog" parent="@android:style/Theme.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
3)初始化、加载布局并添加样式,显示对话框
/**
* 显示
*/
public void showDialog(){
mDialog = new Dialog(mContext, R.style.Theme_audioDialog);
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
View view = layoutInflater.inflate(R.layout.dialog_manager, null);
mDialog.setContentView(view);
mIcon = (ImageView) mDialog.findViewById(R.id.dialog_icon);
mVoice = (ImageView) mDialog.findViewById(R.id.dialog_voice);
mLabel = (TextView) mDialog.findViewById(R.id.recorder_dialogtext);
mDialog.show();
}
4、实现recording、wantToCancel,tooShort与dimiss方法
最主要的就是dialog的view的操作,显示隐藏等
/**
* 录音
*/
public void recording(){
if (mDialog != null && mDialog.isShowing()) {
mIcon.setVisibility(View.VISIBLE);
mVoice.setVisibility(View.VISIBLE);
mLabel.setVisibility(View.VISIBLE);
mIcon.setImageResource(R.drawable.recorder);
mLabel.setText(mContext.getResources().getString(R.string.chat_moveUp));
}
}
/**
* 准备关闭
*/
public void wantToCancel(){
if (mDialog != null && mDialog.isShowing()) {
mIcon.setVisibility(View.VISIBLE);
mVoice.setVisibility(View.GONE);
mLabel.setVisibility(View.VISIBLE);
mIcon.setImageResource(R.drawable.cancel);
mLabel.setText(mContext.getResources().getString(R.string.chat_release));
}
}
/**
* 时间太短提示
*/
public void tooShort(){
if (mDialog != null && mDialog.isShowing()) {
mIcon.setVisibility(View.VISIBLE);
mVoice.setVisibility(View.GONE);
mLabel.setVisibility(View.VISIBLE);
mIcon.setImageResource(R.drawable.voice_to_short);
mLabel.setText(mContext.getResources().getString(R.string.chat_tooShort));
}
}
/**
* 关闭
*/
public void dimiss(){
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
mDialog = null;
}
}
5、更新声音的大小
如图,有七张图片,如何动态获取图片id呢?
int resId = mContext.getResources().getIdentifier(“v” + level, “drawable”, mContext.getPackageName());
/**
* 更新声音的大小
* @param level
*/
public void updateVoiceLevel(int level){
if (mDialog != null && mDialog.isShowing()) {
mIcon.setVisibility(View.VISIBLE);
mVoice.setVisibility(View.VISIBLE);
mLabel.setVisibility(View.VISIBLE);
int resId = mContext.getResources().getIdentifier("v" + level, "drawable", mContext.getPackageName());
mVoice.setImageResource(resId);
}
}
6、在RecordButton里使用DialogHelper
public RecordButton(Context context, AttributeSet attrs) {
super(context, attrs);
mdDialogHelper = new DialogHelper(context);
setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Logger.i(TAG, "onLongClick");
isRecording = true;
mdDialogHelper.showDialog();
return false;
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 按下时,开始播放
changeState(STATE_RECORDING);
break;
case MotionEvent.ACTION_MOVE:
if (isRecording) {
// 根据坐标判断是否要取消
if (wantToCancel(x, y)) {
changeState(STATE_WANT_TO_CANCEL);
} else {
changeState(STATE_RECORDING);
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mCurState == STATE_RECORDING) {
mdDialogHelper.dimiss();
} else if (mCurState == STATE_WANT_TO_CANCEL) {
mdDialogHelper.dimiss();
}
// 恢复状态等信息
reset();
break;
default:
break;
}
return super.onTouchEvent(event);
}
五、声音处理
1、添加权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
2、创建声音帮助类,初步实现:
/**
* @描述 声音处理类,使用单例模式
* @项目名称 App_imooc
* @包名 com.android.imooc.chat
* @类名 AudioHelper
* @author chenlin
* @date 2013年6月15日 下午9:51:21
*/
public class AudioHelper {
private MediaRecorder mRecorder;
private String mDir;
private String mCurrentFilePaht;
//单例模式--------------------------
private static AudioHelper instance;
private AudioHelper(){}
public static AudioHelper getInstance(){
if (instance == null) {
synchronized (AudioHelper.class) {
if (instance == null) {
instance = new AudioHelper();
}
}
}
return instance;
}
//回调函数--------------------------
public interface AudioStateListener {
void onPrepared();
}
private AudioStateListener mListener;
public void setAudioStateListener(AudioStateListener listener){
this.mListener = listener;
}
/**
* 获得声音的等级
* @return
*/
public int getVoiceLevel(){
return 1;
}
/**
* 初始化
*/
public void prepare(){
}
public void record(){
}
public void cancel(){
}
}
3、编写方法prepare:
MediaRecorder:http://developer.android.com/reference/android/media/MediaRecorder.html
MediaRecorder状态转换图:
/**
* 初始化
*/
public void prepare() {
// 找到文件路径
File dir = new File(mDir);
if (!dir.exists()) {
dir.mkdirs();
}
String fileName = generateFileName();
File file = new File(dir, fileName);
// 初始化MediaRecorder
mMediaRecorder = new MediaRecorder();
// 设置各种
mMediaRecorder.setOutputFile(file.getAbsolutePath());
mMediaRecorder.setOutputFile(file.getAbsolutePath());// 设置输出文件
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置MediaRecorder的音频源为麦克风
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);// 设置音频的格式
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);// 设置音频的编码为AMR_NB
// prepare
try {
mMediaRecorder.prepare();
} catch (IllegalStateException e) {
throw new RuntimeException("MediaRecorder 参数错误 " + e.getMessage());
} catch (IOException e) {
throw new RuntimeException("MediaRecorder 不能启动 " + e.getMessage());
}
mMediaRecorder.start();
isPrepared = true;
if (mListener != null) {
mListener.onPrepared();
}
}
4、其他三种方法:
/**
* 获得声音的等级
*
* @return
*/
public int getVoiceLevel(int maxLevel) {
if (isPrepared) {
try {
// getMaxAmplitude(): 1 - 32767
// mMediaRecorder.getMaxAmplitude() / 32768 : 0 - 1;
return (int) (maxLevel * mMediaRecorder.getMaxAmplitude() / 32768 + 0.5f);
} catch (IllegalStateException e) {
throw new RuntimeException("不能获得声音的振幅 " + e.getMessage());
}
}
return 1;
}
public void release() {
if (mMediaRecorder != null) {
mMediaRecorder.stop();
mMediaRecorder.release();
mMediaRecorder = null;
isPrepared = false;
}
}
public void cancel() {
release();
if (mCurrentFilePath != null) {
File file = new File(mCurrentFilePath);
if (file.exists()) {
file.delete();
}
}
mCurrentFilePath = null;
}
六、实现界面
1、创建item布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginTop="5dp" >
<ImageView
android:id="@+id/item_icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="5dp"
android:src="@drawable/icon" />
<FrameLayout
android:id="@+id/recorder_length"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/item_icon"
android:background="@drawable/chatto_bg_focused" >
<View
android:id="@+id/id_recorder_anim"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_gravity="center_vertical|right"
android:background="@drawable/adj"/>
</FrameLayout>
<TextView
android:id="@+id/recorder_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginRight="3dp"
android:layout_toLeftOf="@id/recorder_length"
android:text=""
android:textColor="#ff777777"/>
</RelativeLayout>
2、完善主页:
package com.android.imooc.chat;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.graphics.drawable.AnimationDrawable;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.android.imooc.R;
import com.android.imooc.chat.RecordButton.AudioFinishRecordListener;
/**
* @描述 聊天布局
* @项目名称 App_imooc
* @包名 com.android.imooc.chat
* @类名 ChatActivity
* @author chenlin
* @date 2013年6月13日 下午10:31:59
* @version 1.0
*/
public class ChatActivity extends Activity {
private RecordButton mBtnRecord;
private ListView mlistview;
private ArrayAdapter<Recorder> mAdapter;
private View mView;
private List<Recorder> mDatas = new ArrayList<Recorder>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
mlistview = (ListView) findViewById(R.id.listview);
mAdapter = new RecorderAdapter(this, mDatas);
mlistview.setAdapter(mAdapter);
mBtnRecord = (RecordButton) findViewById(R.id.recordButton);
initEvents();
}
private void initEvents() {
mlistview.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//如果在播放第二动画时,判断原来的动画如果正在播放,就停止
if (mView != null) {
mView.setBackgroundResource(R.drawable.adj);
mView = null;
}
mView = findViewById(R.id.id_recorder_anim);
mView.setBackgroundResource(R.drawable.play_sound);
AnimationDrawable drawable = (AnimationDrawable) mView.getBackground();
drawable.start();
//开始播放声音
MediaHelper.playSound(mDatas.get(position).filePath,new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mView.setBackgroundResource(R.drawable.adj);
}
});
}
});
mBtnRecord.setAudioFinishRecordListener(new AudioFinishRecordListener() {
@Override
public void onFinish(float second, String path) {
Recorder recorder = new Recorder(second, path);
mDatas.add(recorder);
mAdapter.notifyDataSetChanged();
mlistview.setSelection(mDatas.size() - 1);
}
});
}
@Override
protected void onResume() {
MediaHelper.resume();
super.onResume();
}
@Override
protected void onPause() {
MediaHelper.pause();
super.onPause();
}
@Override
protected void onDestroy() {
MediaHelper.release();
super.onDestroy();
}
}
3、实现mediaplayer
package com.android.imooc.chat;
import java.io.IOException;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
/**
* @描述 声音控制类
* @项目名称 App_imooc
* @包名 com.android.imooc.chat
* @类名 MediaHelper
* @author chenlin
* @date 2013年6月17日 下午10:46:01
* @version 1.0
*/
public class MediaHelper {
private static MediaPlayer mPlayer;
private static boolean isPause = false;
public static void playSound(String filePath, OnCompletionListener listener) {
if (mPlayer == null) {
mPlayer = new MediaPlayer();
} else {
mPlayer.reset();
}
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.setOnCompletionListener(listener);
mPlayer.setOnErrorListener(new OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mPlayer.reset();
return false;
}
});
try {
mPlayer.setDataSource(filePath);
mPlayer.prepare();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
throw new RuntimeException("读取文件异常:" + e.getMessage());
}
mPlayer.start();
isPause = false;
}
public static void pause() {
if (mPlayer != null && mPlayer.isPlaying()) {
mPlayer.pause();
isPause = true;
}
}
// 继续
public static void resume() {
if (mPlayer != null && isPause) {
mPlayer.start();
isPause = false;
}
}
public static void release() {
if (mPlayer != null) {
mPlayer.release();
mPlayer = null;
}
}
}
4、控制类recordButton全部代码
/**
* @描述 TODO
* @项目名称 App_imooc
* @包名 com.android.imooc.chat
* @类名 RecordButton
* @author chenlin
* @date 2013年6月13日 下午10:42:50
*/
public class RecordButton extends Button implements AudioStateListener {
private static final String TAG = "RecordButton";
//手指向上移动的距离50,如果超过,说明要cancel
private static final int DISTANCE_Y_CANCEL = 50;
// 按住说话状态
private static final int STATE_NORMAL = 0x00;
// 正在录音状态
private static final int STATE_RECORDING = 0x01;
// 准备取消状态
private static final int STATE_WANT_TO_CANCEL = 0x02;
// 当前状态
private int mCurState = STATE_NORMAL;
// 表示已经录音
private boolean isRecording;
//是否是长按了
private boolean isLongClick;
private DialogHelper mdDialogHelper;
private AudioHelper mAudioHelper;
//--录音完成回调------------------------------------------
public interface AudioFinishRecordListener {
void onFinish(float second, String path);
}
private AudioFinishRecordListener mListener;
public void setAudioFinishRecordListener(AudioFinishRecordListener listener) {
this.mListener = listener;
}
public RecordButton(Context context) {
this(context, null);
}
public RecordButton(Context context, AttributeSet attrs) {
super(context, attrs);
mdDialogHelper = new DialogHelper(context);
String dir = Environment.getExternalStorageDirectory() + File.separator + "chat";
mAudioHelper = AudioHelper.getInstance(dir);
mAudioHelper.setAudioStateListener(this);
setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
isLongClick = true;
mAudioHelper.prepare();
return false;
}
});
}
@Override
public void onPrepared() {
mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);
}
private Runnable mGetVoiceLevelRunnable = new Runnable() {
@Override
public void run() {
while(isRecording){
//SystemClock.sleep(100);
Logger.i(TAG, "run ========" );
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//每个0.1秒更新声音s
mTime += 0.1f;
mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);
}
}
};
private static final int MSG_AUDIO_PREPARED = 0x11;//准备录制声音
private static final int MSG_VOICE_CHANGED = 0x12;//声音改变
private static final int MSG_DIALOG_DIMISS = 0x13;//对话框消失
private float mTime = 0;
private Handler mHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case MSG_AUDIO_PREPARED:
isRecording = true;
//显示声音对话框
mdDialogHelper.showDialog();
//开启线程获取音量
new Thread(mGetVoiceLevelRunnable).start();
break;
case MSG_VOICE_CHANGED:
int level = mAudioHelper.getVoiceLevel(7);
Logger.i(TAG, "level ========" + level);
//在对话框里更新声音的大小
mdDialogHelper.updateVoiceLevel(level);
break;
case MSG_DIALOG_DIMISS:
mdDialogHelper.dimiss();
break;
default:
break;
}
}
};
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 按下时,开始播放
changeState(STATE_RECORDING);
break;
case MotionEvent.ACTION_MOVE:
if (isRecording) {
// 根据坐标判断是否要取消
if (wantToCancel(x, y)) {
changeState(STATE_WANT_TO_CANCEL);
} else {
changeState(STATE_RECORDING);
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
//如果没有长按
if (!isLongClick) {
reset();
return super.onTouchEvent(event);
}
//如果没有录制
if (!isRecording || mTime < 0.6f) {
//显示按的时间太短
mdDialogHelper.tooShort();
//关闭录制
mAudioHelper.cancel();
//发送信息关闭资源
mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS, 1300);
} else if (mCurState == STATE_RECORDING) {
mdDialogHelper.dimiss();
if (mListener != null) {
mListener.onFinish(mTime, mAudioHelper.getFilePath());
}
mAudioHelper.release();
} else if (mCurState == STATE_WANT_TO_CANCEL) {
mdDialogHelper.dimiss();
mAudioHelper.cancel();
}
// 恢复状态等信息
reset();
break;
default:
break;
}
return super.onTouchEvent(event);
}
private void reset() {
isRecording = false;
changeState(STATE_NORMAL);
mTime = 0;
isLongClick = false;
}
private boolean wantToCancel(int x, int y) {
//如果手指不在按钮的x轴范围内
if (x < 0 || x > getWidth()) {
return true;
}
//getHeight是按钮的高度
if (y < -DISTANCE_Y_CANCEL || y > getHeight() + DISTANCE_Y_CANCEL) {
return true;
}
return false;
}
private void changeState(int state) {
if (mCurState != state) {
mCurState = state;
}
switch (state) {
case STATE_NORMAL:
setBackgroundResource(R.drawable.button_recordnormal);
setText(R.string.chat_normal);
break;
case STATE_RECORDING:
setBackgroundResource(R.drawable.button_recording);
setText(R.string.chat_recording);
if (isRecording) {
mdDialogHelper.recording();
}
break;
case STATE_WANT_TO_CANCEL:
setBackgroundResource(R.drawable.button_recording);
setText(R.string.chat_want_to_cancel);
mdDialogHelper.wantToCancel();
break;
default:
break;
}
}
}
七、源码下载:
———————————————————————
(java 架构师全套教程,共760G, 让你从零到架构师,每月轻松拿3万)
有需求者请进站查看,非诚勿扰
https://item.taobao.com/item.htm?spm=686.1000925.0.0.4a155084hc8wek&id=555888526201
01.高级架构师四十二个阶段高
02.Java高级系统培训架构课程148课时
03.Java高级互联网架构师课程
04.Java互联网架构Netty、Nio、Mina等-视频教程
05.Java高级架构设计2016整理-视频教程
06.架构师基础、高级片
07.Java架构师必修linux运维系列课程
08.Java高级系统培训架构课程116课时
(送:hadoop系列教程,java设计模式与数据结构, Spring Cloud微服务, SpringBoot入门)
——————————————————————–