您的位置:首页 > 移动开发 > 微信开发

Android模仿微信语音聊天功能-IT蓝豹

2015-10-16 14:17 656 查看



项目效果如下:








代码如下:

伦理片 http://www.dotdy.com/
AudioManager.java

package com.xuliugen.weichat;

import java.io.File;

import java.io.IOException;

import java.util.UUID;

import android.media.MediaRecorder;

publicclassAudioManager{

privateMediaRecorder mMediaRecorder;

privateString mDir;

privateString mCurrentFilePath;

privatestaticAudioManager mInstance;

privateboolean isPrepare;

privateAudioManager(String dir){

mDir = dir;

}

publicstaticAudioManager getInstance(String dir){

if(mInstance ==null){

synchronized(AudioManager.class){

if(mInstance ==null){

mInstance =newAudioManager(dir);

}

}

}

return mInstance;

}

/** * 使用接口 用于回调 */

publicinterfaceAudioStateListener{

void wellPrepared();

}

publicAudioStateListener mAudioStateListener;

/** * 回调方法 */

publicvoid setOnAudioStateListener(AudioStateListener listener){

mAudioStateListener = listener;

}

// 去准备

publicvoid prepareAudio(){

try{

isPrepare =false;

File dir =newFile(mDir);

if(!dir.exists()){

dir.mkdirs();

}

String fileName = generateFileName();

File file =newFile(dir, fileName);

mCurrentFilePath =file.getAbsolutePath();

mMediaRecorder =newMediaRecorder();

// 设置输出文件

mMediaRecorder.setOutputFile(dir.getAbsolutePath());

// 设置MediaRecorder的音频源为麦克风

mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);

// 设置音频格式

mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);

// 设置音频编码

mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

// 准备录音

mMediaRecorder.prepare();

// 开始

mMediaRecorder.start();

// 准备结束

isPrepare =true;

if(mAudioStateListener !=null){

mAudioStateListener.wellPrepared();

}

}catch(IllegalStateException e){

e.printStackTrace();

}catch(IOException e){

e.printStackTrace();

}

}

/** * 随机生成文件的名称 */

privateString generateFileName(){

return UUID.randomUUID().toString()+".amr";

}

publicint getVoiceLevel(int maxlevel){

if(isPrepare){

try{

// mMediaRecorder.getMaxAmplitude() 1~32767

return maxlevel * mMediaRecorder.getMaxAmplitude()/32768+1;

}catch(Exception e){

}

}

return1;

}

/** * 释放资源 */

publicvoid release(){

//mMediaRecorder.stop();

mMediaRecorder.reset();

mMediaRecorder =null;

}

/** * 取消录音 */

publicvoid cancel(){

release();

if(mCurrentFilePath !=null){

File file =newFile(mCurrentFilePath);

file.delete();

mCurrentFilePath =null;

}

}

publicString getCurrentFilePath(){

return mCurrentFilePath;

}

}

AudioRecorderButton.java

package com.xuliugen.weichat;

import android.content.Context;

import android.os.Environment;

import android.os.Handler;

import android.os.Message;

import android.util.AttributeSet;

import android.view.MotionEvent;

import android.view.View;

import android.widget.Button;

import com.xuliugen.weichat.R;

import com.xuliugen.weichat.AudioManager.AudioStateListener;

publicclassAudioRecorderButtonextendsButton{

privatestaticfinalint STATE_NORMAL =1;//
默认的状态

privatestaticfinalint STATE_RECORDING =2;//
正在录音

privatestaticfinalint STATE_WANT_TO_CANCEL =3;//
希望取消

privateint mCurrentState = STATE_NORMAL;//
当前的状态

privateboolean isRecording =false;//
已经开始录音

privatestaticfinalint DISTANCE_Y_CANCEL =50;

privateDialogManager mDialogManager;

privateAudioManager mAudioManager;

privatefloat mTime;

// 是否触发longClick

privateboolean mReady;

privatestaticfinalint MSG_AUDIO_PREPARED =0x110;

privatestaticfinalint MSG_VOICE_CHANGED =0x111;

privatestaticfinalint MSG_DIALOG_DIMISS =0x112;

/* * 获取音量大小的线程 */

privateRunnable mGetVoiceLevelRunnable =newRunnable(){

publicvoid run(){

while(isRecording){

try{

Thread.sleep(100);

mTime +=0.1f;

mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);

}catch(InterruptedException e){

e.printStackTrace();

}

}

}

};

privateHandler mHandler =newHandler(){

@Override

publicvoid handleMessage(Message msg){

switch(msg.what){

case MSG_AUDIO_PREPARED:

// 显示對話框在开始录音以后

mDialogManager.showRecordingDialog();

isRecording =true;

// 开启一个线程

newThread(mGetVoiceLevelRunnable).start();

break;

case MSG_VOICE_CHANGED:

mDialogManager.updateVoiceLevel(mAudioManager.getVoiceLevel(7));

break;

case MSG_DIALOG_DIMISS:

mDialogManager.dimissDialog();

break;

}

super.handleMessage(msg);

}

};

/** * 以下2个方法是构造方法 */

publicAudioRecorderButton(Context context,AttributeSet attrs){

super(context, attrs);

mDialogManager =newDialogManager(context);

String dir ="/storage/sdcard0/my_weixin";

//String dir = Environment.getExternalStorageDirectory()+"/my_weixin";

mAudioManager =AudioManager.getInstance(dir);

mAudioManager.setOnAudioStateListener(newAudioStateListener(){

publicvoid wellPrepared(){

mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);

}

});

// 由于这个类是button所以在构造方法中添加监听事件

setOnLongClickListener(newOnLongClickListener(){

publicboolean onLongClick(View v){

mReady =true;

mAudioManager.prepareAudio();

returnfalse;

}

});

}

publicAudioRecorderButton(Context context){

this(context,null);

}

/** * 录音完成后的回调 */

publicinterfaceAudioFinishRecorderListener{

void onFinish(float seconds,String filePath);

}

privateAudioFinishRecorderListener audioFinishRecorderListener;

publicvoid setAudioFinishRecorderListener(AudioFinishRecorderListener listener){

audioFinishRecorderListener = listener;

}

/** * 屏幕的触摸事件 */

@Override

publicboolean onTouchEvent(MotionEventevent){

int action =event.getAction();

int x =(int)event.getX();//
获得x轴坐标

int y =(int)event.getY();//
获得y轴坐标

switch(action){

caseMotionEvent.ACTION_DOWN:

changeState(STATE_RECORDING);

break;

caseMotionEvent.ACTION_MOVE:

if(isRecording){

// 如果想要取消,根据x,y的坐标看是否需要取消

if(wantToCancle(x, y)){

changeState(STATE_WANT_TO_CANCEL);

}else{

changeState(STATE_RECORDING);

}

}

break;

caseMotionEvent.ACTION_UP:

if(!mReady){

reset();

returnsuper.onTouchEvent(event);

}

if(!isRecording || mTime <0.6f){

mDialogManager.tooShort();

mAudioManager.cancel();

mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS,1000);//
延迟显示对话框

}elseif(mCurrentState == STATE_RECORDING){//
正在录音的时候,结束

mDialogManager.dimissDialog();

mAudioManager.release();

if(audioFinishRecorderListener !=null){

audioFinishRecorderListener.onFinish(mTime,mAudioManager.getCurrentFilePath());

}

}elseif(mCurrentState == STATE_WANT_TO_CANCEL){//
想要取消

mDialogManager.dimissDialog();

mAudioManager.cancel();

}

reset();

break;

}

returnsuper.onTouchEvent(event);

}

/** * 恢复状态及标志位 */

privatevoid reset(){

isRecording =false;

mTime =0;

mReady =false;

changeState(STATE_NORMAL);

}

privateboolean wantToCancle(int x,int y){

if(x <0|| x > getWidth()){//
超过按钮的宽度

returntrue;

}

// 超过按钮的高度

if(y <-DISTANCE_Y_CANCEL || y > getHeight()+ DISTANCE_Y_CANCEL){

returntrue;

}

returnfalse;

}

/** * 改变 */

privatevoid changeState(int state){

if(mCurrentState != state){

mCurrentState = state;

switch(state){

case STATE_NORMAL:

setBackgroundResource(R.drawable.btn_recorder_normal);

setText(R.string.str_recorder_normal);

break;

case STATE_RECORDING:

setBackgroundResource(R.drawable.btn_recorder_recording);

setText(R.string.str_recorder_recording);

if(isRecording){

mDialogManager.recording();

}

break;

case STATE_WANT_TO_CANCEL:

setBackgroundResource(R.drawable.btn_recorder_recording);

setText(R.string.str_recorder_want_cancel);

mDialogManager.wantToCancel();

break;

}

}

}

}

DialogManager.java

package com.xuliugen.weichat;

import android.app.AlertDialog;

import android.content.Context;

import android.view.LayoutInflater;

import android.view.View;

import android.widget.ImageView;

import android.widget.TextView;

import com.xuliugen.weichat.R;

/** * 用于管理Dialog * * @author xuliugen * */

publicclassDialogManager{

privateAlertDialog.Builder builder;

privateImageView mIcon;

privateImageView mVoice;

privateTextView mLable;

privateContext mContext;

privateAlertDialog dialog;//用于取消AlertDialog.Builder

/** * 构造方法 传入上下文 */

publicDialogManager(Context context){

this.mContext = context;

}

// 显示录音的对话框

publicvoid showRecordingDialog(){

builder =newAlertDialog.Builder(mContext, R.style.AudioDialog);

LayoutInflater inflater =LayoutInflater.from(mContext);

View view = inflater.inflate(R.layout.dialog_recorder,null);

mIcon =(ImageView) view.findViewById(R.id.id_recorder_dialog_icon);

mVoice =(ImageView) view.findViewById(R.id.id_recorder_dialog_voice);

mLable =(TextView) view.findViewById(R.id.id_recorder_dialog_label);

builder.setView(view);

builder.create();

dialog = builder.show();

}

publicvoid recording(){

if(dialog !=null&& dialog.isShowing()){//显示状态

mIcon.setVisibility(View.VISIBLE);

mVoice.setVisibility(View.VISIBLE);

mLable.setVisibility(View.VISIBLE);

mIcon.setImageResource(R.drawable.recorder);

mLable.setText("手指上滑,取消发送");

}

}

// 显示想取消的对话框

publicvoid wantToCancel(){

if(dialog !=null&& dialog.isShowing()){//显示状态

mIcon.setVisibility(View.VISIBLE);

mVoice.setVisibility(View.GONE);

mLable.setVisibility(View.VISIBLE);

mIcon.setImageResource(R.drawable.cancel);

mLable.setText("松开手指,取消发送");

}

}

// 显示时间过短的对话框

publicvoid tooShort(){

if(dialog !=null&& dialog.isShowing()){//显示状态

mIcon.setVisibility(View.VISIBLE);

mVoice.setVisibility(View.GONE);

mLable.setVisibility(View.VISIBLE);

mIcon.setImageResource(R.drawable.voice_to_short);

mLable.setText("录音时间过短");

}

}

// 显示取消的对话框

publicvoid dimissDialog(){

if(dialog !=null&& dialog.isShowing()){//显示状态

dialog.dismiss();

dialog =null;

}

}

// 显示更新音量级别的对话框

publicvoid updateVoiceLevel(int level){

if(dialog !=null&& dialog.isShowing()){//显示状态

// mIcon.setVisibility(View.VISIBLE);

// mVoice.setVisibility(View.VISIBLE);

// mLable.setVisibility(View.VISIBLE);

//设置图片的id

int resId = mContext.getResources().getIdentifier("v"+level,"drawable", mContext.getPackageName());

mVoice.setImageResource(resId);

}

}

}

MainActivity.java

package com.xuliugen.weichat;

import java.util.ArrayList;

import java.util.List;

import com.xuliugen.weichat.AudioRecorderButton.AudioFinishRecorderListener;

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.ArrayAdapter;

import android.widget.ListView;

import android.widget.AdapterView.OnItemClickListener;

publicclassMainActivityextendsActivity{

privateListView mListView;

privateArrayAdapter<Recorder> mAdapter;

privateList<Recorder> mDatas =newArrayList<MainActivity.Recorder>();

privateAudioRecorderButton mAudioRecorderButton;

privateView animView;

@Override

protectedvoid onCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mListView =(ListView) findViewById(R.id.id_listview);

mAudioRecorderButton =(AudioRecorderButton) findViewById(R.id.id_recorder_button);

mAudioRecorderButton.setAudioFinishRecorderListener(newAudioFinishRecorderListener(){

publicvoid onFinish(float seconds,String filePath){

Recorder recorder =newRecorder(seconds, filePath);

mDatas.add(recorder);

mAdapter.notifyDataSetChanged();//通知更新的内容

mListView.setSelection(mDatas.size()-1);//将lisview设置为最后一个

}

});

mAdapter =newRecoderAdapter(this, mDatas);

mListView.setAdapter(mAdapter);

//listView的item点击事件

mListView.setOnItemClickListener(newOnItemClickListener(){

publicvoid onItemClick(AdapterView<?> arg0,View view,int position,long id){

// 播放动画(帧动画)

if(animView !=null){

animView.setBackgroundResource(R.drawable.adj);

animView =null;

}

animView = view.findViewById(R.id.id_recoder_anim);

animView.setBackgroundResource(R.drawable.play_anim);

AnimationDrawable animation =(AnimationDrawable) animView.getBackground();

animation.start();

// 播放录音

MediaManager.playSound(mDatas.get(position).filePath,newMediaPlayer.OnCompletionListener(){

publicvoid onCompletion(MediaPlayer mp){

animView.setBackgroundResource(R.drawable.adj);

}

});

}

});

}

@Override

protectedvoid onPause(){

super.onPause();

MediaManager.pause();

}

@Override

protectedvoid onResume(){

super.onResume();

MediaManager.resume();

}

@Override

protectedvoid onDestroy(){

super.onDestroy();

MediaManager.release();

}

classRecorder{

float time;

String filePath;

publicRecorder(float time,String filePath){

super();

this.time = time;

this.filePath = filePath;

}

publicfloat getTime(){

return time;

}

publicvoid setTime(float time){

this.time = time;

}

publicString getFilePath(){

return filePath;

}

publicvoid setFilePath(String filePath){

this.filePath = filePath;

}

}

}

MediaManager.java

package com.xuliugen.weichat;

import android.media.AudioManager;

import android.media.MediaPlayer;

import android.media.MediaPlayer.OnCompletionListener;

import android.media.MediaPlayer.OnErrorListener;

publicclassMediaManager{

privatestaticMediaPlayer mMediaPlayer;

privatestaticboolean isPause;

/** * 播放音乐 * @param filePath * @param onCompletionListener */

publicstaticvoid playSound(String filePath,OnCompletionListener onCompletionListener){

if(mMediaPlayer ==null){

mMediaPlayer =newMediaPlayer();

//设置一个error监听器

mMediaPlayer.setOnErrorListener(newOnErrorListener(){

publicboolean onError(MediaPlayer arg0,int arg1,int arg2){

mMediaPlayer.reset();

returnfalse;

}

});

}else{

mMediaPlayer.reset();

}

try{

mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

mMediaPlayer.setOnCompletionListener(onCompletionListener);

mMediaPlayer.setDataSource(filePath);

mMediaPlayer.prepare();

mMediaPlayer.start();

}catch(Exception e){

}

}

/** * 暂停播放 */

publicstaticvoid pause(){

if(mMediaPlayer !=null&& mMediaPlayer.isPlaying()){//正在播放的时候

mMediaPlayer.pause();

isPause =true;

}

}

/** * 当前是isPause状态 */

publicstaticvoid resume(){

if(mMediaPlayer !=null&& isPause){

mMediaPlayer.start();

isPause =false;

}

}

/** * 释放资源 */

publicstaticvoid release(){

if(mMediaPlayer !=null){

mMediaPlayer.release();

mMediaPlayer =null;

}

}

}

RecoderAdapter.java

package com.xuliugen.weichat;

import java.util.List;

import android.content.Context;

import android.util.DisplayMetrics;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.view.WindowManager;

import android.widget.ArrayAdapter;

import android.widget.TextView;

import com.xuliugen.weichat.MainActivity.Recorder;

publicclassRecoderAdapterextendsArrayAdapter<Recorder>{

privateContext mContext;

privateList<Recorder> mDatas;

privateint mMinItemWidth;//最小的item宽度

privateint mMaxItemWidth;//最大的item宽度

privateLayoutInflater mInflater;

publicRecoderAdapter(Context context,List<Recorder> datas){

super(context,-1, datas);

mContext = context;

mDatas = datas;

//获取屏幕的宽度

WindowManager wm =(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

DisplayMetrics outMetrics =newDisplayMetrics();

wm.getDefaultDisplay().getMetrics(outMetrics);

mMaxItemWidth =(int)(outMetrics.widthPixels *0.7f);

mMinItemWidth =(int)(outMetrics.widthPixels *0.15f);

mInflater =LayoutInflater.from(context);

}

/** * 定义一个ViewHolder */

privateclassViewHolder{

TextView seconds;// 时间

View length;// 长度

}

@Override

publicView getView(int position,View convertView,ViewGroup parent){

ViewHolder holder =null;

if(convertView ==null){

convertView = mInflater.inflate(R.layout.item_recoder, parent,false);

holder =newViewHolder();

holder.seconds =(TextView) convertView.findViewById(R.id.id_recoder_time);

holder.length = convertView.findViewById(R.id.id_recoder_lenght);

convertView.setTag(holder);

}else{

holder =(ViewHolder) convertView.getTag();

}

holder.seconds.setText(Math.round(getItem(position).time)+"\"");

ViewGroup.LayoutParams lp = holder.length.getLayoutParams();

lp.width =(int)(mMinItemWidth +(mMaxItemWidth /60f)* getItem(position).time);

return convertView;

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: