![](https://i-blog.csdnimg.cn/blog_migrate/eefee02b0ad18718f4608b9d9d9471b9.png)
1.首先是主页面的布局
布局采用线性布局,上面使用的一个ListView,下面使用的是一个自定义的Button(会在下面进行介绍)
- <span style="font-size:18px;"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- tools:context="com.bwie.liaotian_test2.MainActivity">
- <ListView
- android:id="@+id/lv"
- android:dividerHeight="10dp"
- android:divider="@null"
- android:background="#ebebeb"
- android:layout_weight="1"
- android:layout_width="match_parent"
- android:layout_height="0dp"></ListView>
- <FrameLayout
- android:background="#fff"
- android:id="@+id/frame"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <com.bwie.liaotian_test2.View.AudioRecorderButton
- android:minHeight="0dp"
- android:id="@+id/audio_button"
- android:layout_marginRight="50dp"
- android:layout_marginLeft="50dp"
- android:layout_marginTop="6dp"
- android:layout_marginBottom="7dp"
- android:layout_width="match_parent"
- android:gravity="center"
- android:padding="6dp"
- android:textSize="20sp"
- android:textColor="#727272"
- android:background="@drawable/btn_normal"
- android:text="@string/str_recorder_normal"
- android:layout_height="wrap_content" />
- <View
- android:background="#ccc"
- android:layout_width="match_parent"
- android:layout_height="1dp"/>
- </FrameLayout>
- </LinearLayout></span>
btn_normal
- <span style="font-size:18px;"><shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="#ffffff"/>
- <stroke android:color="#9b9b9b" android:width="1px"/>
- <corners android:radius="3dp"/>
- </shape></span>
btn_recording
- <span style="font-size:18px;"><shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="#bbcc00"/>
- <stroke android:color="#9b9b9b" android:width="1px"/>
- <corners android:radius="3dp"/>
- </shape></span>
- <span style="font-size:18px;">public class AudioRecorderButton extends android.support.v7.widget.AppCompatButton implements AudioManager.AudioStateListener {
- private static final int STATE_NORMAL = 1;//正常状态
- private static final int STATE_RECORDING = 2;//录音状态
- private static final int STATE_WANT_TO_CANCEL = 3;//取消状态
- private int mCurState = STATE_NORMAL;//当前状态
- private boolean isRecording = false;//是否正在录音
- private static final int DISTANXE_Y_CANCEL = 50;
- private AudioManager mAudioManager;
- private DialogManager mDialogManger;
- private boolean mReady = false;//是否触发longClick
- private float mTime;//计时
- public AudioRecorderButton(Context context) {
- super(context,null);
- }
- public AudioRecorderButton(Context context, AttributeSet attrs) {
- super(context, attrs);
- String dir = Environment.getExternalStorageDirectory() + "/my_recorder_audios";
- mAudioManager = AudioManager.getmInstance(dir);
- mAudioManager.setAudioStateListener(this);
- mDialogManger=new DialogManager(context);
- setOnLongClickListener(new OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- mAudioManager.prepareAudio();
- mReady = true;
- return false;
- }
- });
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- int action = event.getAction();
- int x= (int) event.getX();
- int y= (int) event.getY();
- switch (action){
- case MotionEvent.ACTION_DOWN:
- changeSate(STATE_RECORDING);
- break;
- case MotionEvent.ACTION_MOVE:
- if (isRecording) {
- if (isCancelRecorder(x, y)) {
- changeSate(STATE_WANT_TO_CANCEL);
- } else {
- changeSate(STATE_RECORDING);
- }
- }
- break;
- case MotionEvent.ACTION_UP:
- if(!mReady){
- reset();
- return super.onTouchEvent(event);
- }
- if (!isRecording || mTime < 0.6f) {
- mDialogManger.tooShort();
- mAudioManager.cancel();
- mHandler.sendEmptyMessageDelayed(MSG_LODING_DISMISS, 1000);
- } else if (mCurState == STATE_RECORDING) {//正常录制结束
- mDialogManger.dimissDialog();
- mAudioManager.release();
- if (mListener != null) {
- mListener.onFinish(mTime, mAudioManager.getmCurrentFilePath());
- }
- } else if (mCurState == STATE_WANT_TO_CANCEL) {
- mDialogManger.dimissDialog();
- mAudioManager.cancel();
- }
- reset();
- break;
- }
- return super.onTouchEvent(event);
- }
- /**
- * 根据移动后的位置,判断是否取消录音
- */
- private boolean isCancelRecorder(int x, int y) {
- if(x<0||x>getWidth()){
- return true;
- }
- if(y<-DISTANXE_Y_CANCEL||y>getHeight()+DISTANXE_Y_CANCEL){
- return true;
- }
- return false;
- }
- /**
- * 根据不同状态,更改不同的文字和显示的背景
- */
- private void changeSate(int state) {
- if (mCurState != state) {
- mCurState = state;
- switch (state) {
- case STATE_NORMAL:
- setBackgroundResource(R.drawable.btn_normal);
- setText(R.string.str_recorder_normal);
- break;
- case STATE_RECORDING:
- setBackgroundResource(R.drawable.btn_recording);
- setText(R.string.str_recorder_recording);
- if (isRecording) {
- mDialogManger.recording();
- }
- break;
- case STATE_WANT_TO_CANCEL:
- setBackgroundResource(R.drawable.btn_recording);
- setText(R.string.str_recorder_want_cancel);
- mDialogManger.wantToCancel();
- break;
- }
- }
- }
- /**
- * 重置标识位
- */
- private void reset() {
- changeSate(STATE_NORMAL);
- isRecording = false;
- mReady = false;
- mTime = 0;
- }
- /**
- * 开始播放时回调此方法
- */
- @Override
- public void wellPrepared() {
- mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);
- }
- private static final int MSG_AUDIO_PREPARED = 0x110;
- private static final int MSG_VOICE_CHAGE = 0x111;
- private static final int MSG_LODING_DISMISS = 0x112;
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch (msg.what) {
- case MSG_AUDIO_PREPARED:
- mDialogManger.showRecordeingDialog();
- isRecording = true;
- new Thread(mGetVoiceLevelRunnable).start();
- break;
- case MSG_VOICE_CHAGE:
- mDialogManger.updateVoiceLevel(mAudioManager.getVoiceLevel(7));
- break;
- case MSG_LODING_DISMISS:
- mDialogManger.dimissDialog();
- break;
- }
- }
- };
- /**
- * 获取音量大小,并计时
- */
- private Runnable mGetVoiceLevelRunnable = new Runnable() {
- @Override
- public void run() {
- while (isRecording) {
- SystemClock.sleep(100);
- mTime += 0.1f;
- mHandler.sendEmptyMessage(MSG_VOICE_CHAGE);
- }
- }
- };
- /**
- * 完成录制后的回调接口
- */
- public interface AudioFinishRecorderListener {
- void onFinish(float time, String filePath);
- }
- private AudioFinishRecorderListener mListener;
- public void setAudioFinishRecorderListener(AudioFinishRecorderListener listener) {
- mListener = listener;
- }
- }</span>
- <span style="font-size:18px;"><string name="str_recorder_normal">按住说话</string>
- <string name="str_recorder_recording">松开结束</string>
- <string name="str_recorder_want_cancel">松开手指,取消发送</string>
- <string name="ss">手指上滑,取消发送</string></span>
dialog.xml
- <span style="font-size:18px;"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:orientation="vertical"
- android:padding="20dp"
- android:gravity="center"
- android:background="@drawable/dialog_loading_bg"
- android:layout_height="wrap_content">
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
- <ImageView
- android:id="@+id/iv1"
- android:visibility="visible"
- android:src="@drawable/recorder"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- <ImageView
- android:id="@+id/iv2"
- android:visibility="visible"
- android:src="@drawable/v1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </LinearLayout>
- <TextView
- android:id="@+id/tv"
- android:textColor="#ffff"
- android:text="@string/ss"
- android:layout_marginTop="5dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </LinearLayout></span>
- <span style="font-size:18px;"><style name="Theme_AudioDialog">
- <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></span>
DialogManager
- <span style="font-size:18px;">public class DialogManager {
- private Dialog mDialog;
- private ImageView mIcon;
- private ImageView mVoice;
- private TextView mLabel;
- private Context mContext;
- public DialogManager(Context mContext) {
- this.mContext = mContext;
- }
- /**
- * 显示对话框
- */
- public void showRecordeingDialog(){
- mDialog = new Dialog(mContext, R.style.Theme_AudioDialog);
- LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(mContext.LAYOUT_INFLATER_SERVICE);
- View view = inflater.inflate(R.layout.dialog, null);
- mDialog.setContentView(view);
- //获取控件
- mIcon = (ImageView) mDialog.findViewById(R.id.iv1);
- mVoice = (ImageView) mDialog.findViewById(R.id.iv2);
- mLabel = (TextView) mDialog.findViewById(R.id.tv);
- //显示
- mDialog.show();
- }
- /**
- * 正在录制提示
- */
- 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(R.string.ss);
- }
- }
- /**
- * 取消录制对话框提示
- */
- 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("松开手指,取消发送");
- }
- }
- /**
- * 录音时间过短提示
- */
- 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("录音时间过短");
- }
- }
- /**
- * 取消对话框
- */
- public void dimissDialog(){
- if (mDialog != null && mDialog.isShowing()) {
- mDialog.dismiss();
- mDialog=null;
- }
- }
- /**
- * 显示音量大小
- */
- public void updateVoiceLevel(int level){
- if (mDialog != null && mDialog.isShowing()) {
- int resId = mContext.getResources().getIdentifier("v" + level, "drawable", mContext.getPackageName());
- mVoice.setImageResource(resId);
- }
- }
- }</span>
来控制录制状态。------AudioManager
- <span style="font-size:18px;">public class AudioManager {
- private MediaRecorder mMediaRecorder;
- private String mDir;// 保存的目录
- private String mCurrentFilePath;// 保存音频文件的全路径
- private boolean isPrepared = false;// 是否准备完毕
- private static AudioManager mInstance;
- private AudioManager(String dir) {
- mDir = dir;
- }
- public static AudioManager getmInstance(String mDir) {
- if (mInstance == null) {
- synchronized (AudioManager.class) {
- if (mInstance == null) {
- mInstance = new AudioManager(mDir);
- }
- }
- }
- return mInstance;
- }
- /**
- * 准备完毕的回调
- */
- public interface AudioStateListener {
- void wellPrepared();
- }
- private AudioStateListener mListener;
- public void setAudioStateListener(AudioStateListener listener) {
- mListener = listener;
- }
- /** 准备录制 */
- public void prepareAudio() {
- try {
- isPrepared = false;
- File dir = new File(mDir);
- if (!dir.exists()) {
- dir.mkdirs();
- }
- String fileName = generateName();
- File file = new File(dir, fileName);
- mCurrentFilePath = file.getAbsolutePath();
- mMediaRecorder = new MediaRecorder();
- // 设置输出文件
- mMediaRecorder.setOutputFile(mCurrentFilePath);
- // 设置音频源为麦克风
- mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
- // 设置音频格式
- mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
- // 设置音频编码
- mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
- mMediaRecorder.prepare();
- mMediaRecorder.start();
- isPrepared = true;
- if (mListener != null) {
- mListener.wellPrepared();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /** 获取音量大小 */
- public int getVoiceLevel(int maxLevel) {
- if (isPrepared) {
- try {
- //mMediaRecorder.getMaxAmplitude() 1-32767
- //注意此处mMediaRecorder.getMaxAmplitude 只能取一次,如果前面取了一次,后边再取就为0了
- return ((mMediaRecorder.getMaxAmplitude() * maxLevel) / 32768) + 1;
- } catch (Exception e) {
- }
- }
- return 1;
- }
- /** 保存录音,释放资源 */
- public void release() {
- if(mMediaRecorder != null) {
- mMediaRecorder.stop();
- mMediaRecorder.release();
- mMediaRecorder = null;
- }
- }
- /** 取消录制 */
- public void cancel() {
- release();
- if(mCurrentFilePath != null) {
- File file = new File(mCurrentFilePath);
- if(file.exists()) {
- file.delete();
- mCurrentFilePath = null;
- }
- }
- }
- /** 获取录制音频的总路径 */
- public String getmCurrentFilePath(){
- return mCurrentFilePath;
- }
- /**
- * 生成一个随机名字
- */
- private String generateName() {
- return UUID.randomUUID().toString() + ".amr";
- }
- }</span>
5.1 bean
- <span style="font-size:18px;">public class Recorder {
- private float time;
- private String filePath;
- public Recorder(float time, String filePath) {
- this.time = time;
- this.filePath = filePath;
- }
- public float getTime() {
- return time;
- }
- public void setTime(float time) {
- this.time = time;
- }
- public String getFilePath() {
- return filePath;
- }
- public void setFilePath(String filePath) {
- this.filePath = filePath;
- }
- }
- </span>
- <span style="font-size:18px;"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_marginTop="5dp"
- android:layout_height="60dp">
- <ImageView
- android:src="@drawable/icon"
- android:layout_alignParentRight="true"
- android:layout_centerVertical="true"
- android:layout_marginRight="5dp"
- android:id="@+id/icon_iv"
- android:layout_width="40dp"
- android:layout_height="40dp" />
- <FrameLayout
- android:layout_toLeftOf="@+id/icon_iv"
- android:layout_centerVertical="true"
- android:id="@+id/fragme"
- android:layout_width="wrap_content"
- android:layout_height="50dp"
- android:background="@drawable/chatto_bg_focused">
- <View
- android:background="@drawable/adj"
- android:layout_gravity="center_vertical|right"
- android:id="@+id/icon_v"
- android:layout_width="25dp"
- android:layout_height="25dp"/>
- </FrameLayout>
- <TextView
- android:textColor="#ff777777"
- android:layout_marginRight="3dp"
- android:layout_toLeftOf="@+id/fragme"
- android:layout_centerVertical="true"
- android:id="@+id/icon_time"
- android:textSize="18sp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </RelativeLayout>
- </span>
- <span style="font-size:18px;">public class RecorderAdapter extends ArrayAdapter<Recorder> {
- private List<Recorder> mDatas;
- private Context mContext;
- private int mMinItemWidhth;
- private int mMaxItemWidhth;
- public RecorderAdapter(Context context, List<Recorder> datas) {
- super(context, -1, datas);
- mDatas = datas;
- mContext = context;
- WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- DisplayMetrics outMetrics = new DisplayMetrics();
- wm.getDefaultDisplay().getMetrics(outMetrics);
- mMaxItemWidhth = (int) (outMetrics.widthPixels * 0.7f);
- mMinItemWidhth = (int) (outMetrics.widthPixels * 0.15f);
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- ViewHolder holder=null;
- if(holder==null) {
- holder=new ViewHolder();
- if (convertView == null) {
- convertView = View.inflate(mContext,R.layout.item_re, null);
- holder.length=convertView.findViewById(R.id.fragme);
- holder.time=convertView.findViewById(R.id.icon_time);
- convertView.setTag(holder);
- }else{
- holder= (ViewHolder) convertView.getTag();
- }
- }
- holder.time.setText(Math.round(getItem(position).getTime())+ "\"");
- ViewGroup.LayoutParams layoutParams = holder.length.getLayoutParams();
- layoutParams.width = (int) (mMinItemWidhth + (mMaxItemWidhth / 60f * getItem(position).getTime()));
- return convertView;
- }
- private class ViewHolder{
- public TextView time;
- private View length;
- public TextView getTime() {
- return time;
- }
- public void setTime(TextView time) {
- this.time = time;
- }
- public View getLength() {
- return length;
- }
- public void setLength(View length) {
- this.length = length;
- }
- }
- }</span>
- <span style="font-size:18px;">public class MediaManager {
- private static MediaPlayer mMediaPlayer;
- private static boolean isPause = false;//是否是暂停
- /**
- * 播放音频
- */
- public static void playSound(String filePath, MediaPlayer.OnCompletionListener onCompletionListener) {
- if (mMediaPlayer == null) {
- mMediaPlayer = new MediaPlayer();
- mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
- @Override
- public boolean onError(MediaPlayer mp, int what, int extra) {
- mMediaPlayer.reset();
- return false;
- }
- });
- } else {
- mMediaPlayer.reset();
- }
- try {
- mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
- mMediaPlayer.setOnCompletionListener(onCompletionListener);
- mMediaPlayer.setDataSource(filePath);
- mMediaPlayer.prepare();
- mMediaPlayer.start();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 暂停
- */
- public static void pause() {
- if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
- mMediaPlayer.pause();
- isPause = true;
- }
- }
- /**
- * 继续
- */
- public static void resume() {
- if (mMediaPlayer != null && isPause) {
- mMediaPlayer.start();
- isPause = false;
- }
- }
- /**
- * 释放资源
- */
- public static void release() {
- if (mMediaPlayer != null) {
- mMediaPlayer.release();
- mMediaPlayer = null;
- }
- }
- }</span>
- <span style="font-size:18px;">public class MainActivity extends AppCompatActivity {
- private ListView lv;
- private ArrayAdapter<Recorder> mAdapter;
- private List<Recorder> list=new ArrayList<>();
- private AudioRecorderButton audioRecorderButton;
- private View animView;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- lv = findViewById(R.id.lv);
- audioRecorderButton=findViewById(R.id.audio_button);
- audioRecorderButton.setAudioFinishRecorderListener(new AudioRecorderButton.AudioFinishRecorderListener() {
- @Override
- public void onFinish(float time, String filePath) {
- Recorder recorder=new Recorder(time,filePath);
- list.add(recorder);
- mAdapter.notifyDataSetChanged();
- lv.setSelection(list.size()-1);
- }
- });
- mAdapter=new RecorderAdapter(this,list);
- lv.setAdapter(mAdapter);
- lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if(animView!=null){
- animView.setBackgroundResource(R.drawable.adj);
- animView=null;
- }
- //播放动画
- animView = view.findViewById(R.id.icon_v);
- animView.setBackgroundResource(R.drawable.play_anim);
- AnimationDrawable anim= (AnimationDrawable) animView.getBackground();
- anim.start();
- // 播放音频
- MediaManager.playSound(list.get(position).getFilePath(), new MediaPlayer.OnCompletionListener() {
- @Override
- public void onCompletion(MediaPlayer mp) {
- animView.setBackgroundResource(R.drawable.adj);
- }
- });
- }
- });
- }
- @Override
- protected void onPause() {
- MediaManager.pause();
- super.onPause();
- }
- @Override
- protected void onResume() {
- MediaManager.resume();
- super.onResume();
- }
- @Override
- protected void onDestroy() {
- MediaManager.release();
- super.onDestroy();
- }
- }</span>
- <span style="font-size:18px;"><animation-list xmlns:android="http://schemas.android.com/apk/res/android"
- >
- <item
- android:drawable="@mipmap/v_anim1"
- android:duration="300"/>
- <item
- android:drawable="@mipmap/v_anim2"
- android:duration="300"/>
- <item
- android:drawable="@mipmap/v_anim3"
- android:duration="300"/>
- </animation-list> </span>
- <span style="font-size:18px;"> <uses-permission android:name="android.permission.RECORD_AUDIO"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> </span>