位置: 编程技术 - 正文
推荐整理分享仿微信语音语音聊天(仿微信语音聊天),希望有所帮助,仅作参考,欢迎阅读内容。
文章相关热门搜索词:模拟微信语音通话,微信语音模仿声音,模仿微信语音,模仿微信语音,仿微信语音语音的软件,模仿微信语音,仿微信语音语音怎么设置,仿微信语音语音的软件,内容如对您有帮助,希望把文章链接给更多的朋友!
仿微信语音聊天,录音结束后显示在listview上,点击item可进行播放。
一。效果图
二、代码
2.1 activity_main.xml的编写
<LinearLayout xmlns:android=" xmlns:tools=" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ListView android:id="@id/listview" android:layout_width="fill_parent" android:layout_height="0dp" android:layout_weight="1" > </ListView> <Button android:id="@id/button" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="测试录音" /></LinearLayout>
2.2 mainactivity.java的编写
package com.recorderactivity;import java.util.ArrayList;import java.util.List;import com.recorderactivity.AudioManager.AudioStateListener;import android.R.integer;import android.annotation.SuppressLint;import android.app.Activity;import android.graphics.drawable.AnimationDrawable;import android.media.MediaPlayer;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.os.Message;import android.provider.ContactsContract.CommonDataKinds.Event;import android.view.Menu;import android.view.MenuItem;import android.view.MotionEvent;import android.view.View;import android.view.View.OnLongClickListener;import android.view.View.OnTouchListener;import android.view.Window;import android.widget.Adapter;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.Button;import android.widget.ListView;@SuppressLint({ "ClickableViewAccessibility", "HandlerLeak" })public class MainActivity extends Activity {private Button button;private ListView listview;private ListViewAdapter mAdapter;private List<RecorderEntity> mDatas = new ArrayList<RecorderEntity>();/*** 录音的三种状态*/private static final int STATE_NORMAL = 1;// 默认状态private static final int STATE_RECORDING = 2;// 录音状态private static final int STATE_CANCEL = 3;// 取消状态private int mCurState = STATE_NORMAL;// 默认状态private boolean isRecording = false;// 判断是否录音private static final int DISTANCE_TO_CANCEL = ;private DialogManager mDialogManager;private AudioManager mAudioManager;private String dir = Environment.getExternalStorageDirectory() "/RecordActivity";private static final int MSG_AUDIO_PREPARED = 0X;private static final int MSG_VOICE_CHANGED = 0X;private static final int MSG_DIALOG_DIMISS = 0X;private Handler mHandler;private float mTime;private boolean mReady;// 是否触发onlongclickprivate View mAnimView;// 播放动画view@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);initView();initDate();setListener();}/*** @Description:初始化控件* @author :吴雄峰* */private void initView() {// TODO 自动生成的方法存根button = (Button) findViewById(R.id.button);listview = (ListView) findViewById(R.id.listview);}/*** @Description:初始化数据* @author :吴雄峰* */private void initDate() {mDialogManager = new DialogManager(MainActivity.this);mAudioManager = AudioManager.getInstance(dir);mAdapter = new ListViewAdapter(getApplicationContext(), mDatas);listview.setAdapter(mAdapter);mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {// TODO 自动生成的方法存根switch (msg.what) {case MSG_AUDIO_PREPARED:mDialogManager.showRecordingDialog();isRecording = true;new Thread(mGetVoiceLevelRunnable).start();break;case MSG_VOICE_CHANGED:mDialogManager.updateVoiceLevel(mAudioManager.getVoiceLevel(7));break;case MSG_DIALOG_DIMISS:mDialogManager.dimissDialog();break;default:break;}}};}/*** @Description:设置监听* @author :吴雄峰* */private void setListener() {// TODO 自动生成的方法存根button.setOnLongClickListener(new OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {mReady = true;mAudioManager.prepareAudio();return false;}});button.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {// TODO 自动生成的方法存根int action = event.getAction();// 获取坐标int x = (int) event.getX();int y = (int) event.getY();switch (action) {case MotionEvent.ACTION_DOWN:changeState(STATE_RECORDING);break;case MotionEvent.ACTION_MOVE:if (isRecording) {// 已经开始录音// 根据x,y的坐标判断是否想要取消if (wantToCancel(x, y)) {changeState(STATE_CANCEL);} else {changeState(STATE_RECORDING);}}break;case MotionEvent.ACTION_UP:if (!mReady) {reSet();return onTouchEvent(event);}if (!isRecording || mTime < 0.6f) {// 时间过短,录音还没开始mDialogManager.tooShort();mAudioManager.cancel();mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS,);} else if (mCurState == STATE_RECORDING) {// 正常结束mDialogManager.dimissDialog();mAudioManager.release();// 添加数据到listview中RecorderEntity mEntity = new RecorderEntity(mTime,mAudioManager.getCurrentFilePath());mDatas.add(mEntity);mAdapter.notifyDataSetChanged();listview.setSelection(mDatas.size() - 1);} else if (mCurState == STATE_CANCEL) {// 取消mDialogManager.dimissDialog();mAudioManager.cancel();}reSet();break;default:break;}return false;}});mAudioManager.setOnAudioStateListener(new AudioStateListener() {@Overridepublic void wellPrepared() {// TODO 自动生成的方法存根mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);}});listview.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {if (mAnimView != null) {mAnimView.setBackgroundResource(R.drawable.adj);mAnimView = null;}// 播放动画,帧动画去实现mAnimView = view.findViewById(R.id.id_recorder_anim);mAnimView.setBackgroundResource(R.drawable.play_anim);AnimationDrawable animationDrawable = (AnimationDrawable) mAnimView.getBackground();animationDrawable.start();// 播放音频MediaManager.playSound(mDatas.get(position).getmFilePath(),new MediaPlayer.OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mp) {mAnimView.setBackgroundResource(R.drawable.adj);}});}});}@Overrideprotected void onPause() {// TODO 自动生成的方法存根super.onPause();MediaManager.pause();}@Overrideprotected void onResume() {// TODO 自动生成的方法存根super.onResume();MediaManager.resume();}@Overrideprotected void onDestroy() {// TODO 自动生成的方法存根super.onDestroy();MediaManager.release();}/*** 获取音量大小的runnable*/private Runnable mGetVoiceLevelRunnable = new Runnable() {@Overridepublic void run() {// TODO 自动生成的方法存根while (isRecording) {try {Thread.sleep();mTime = 0.1f;mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);} catch (Exception e) {// TODO: handle exception}}}};/*** @Description: 恢复状态及标志位* @author :吴雄峰* */protected void reSet() {// TODO 自动生成的方法存根isRecording = false;changeState(STATE_NORMAL);mTime = 0;mReady = false;}/*** @Description: 根据坐标判断是否要取消* @author :吴雄峰* */protected boolean wantToCancel(int x, int y) {if (x < 0 || x > button.getWidth()) {return true;}if (y < -DISTANCE_TO_CANCEL|| y > button.getHeight() DISTANCE_TO_CANCEL) {return true;}return false;}/*** @Description:改变状态* @author :吴雄峰* */protected void changeState(int state) {// TODO 自动生成的方法存根if (mCurState != state) {mCurState = state;switch (state) {case STATE_NORMAL:button.setBackgroundResource(R.drawable.button_recorder_normal);button.setText(R.string.str_recorder_normal);break;case STATE_RECORDING:button.setBackgroundResource(R.drawable.button_recorder_recording);button.setText(R.string.str_recorder_recordering);if (isRecording) {mDialogManager.recording();}break;case STATE_CANCEL:button.setBackgroundResource(R.drawable.button_recorder_recording);button.setText(R.string.str_recorder_want_to_cancel);if (isRecording) {mDialogManager.wantToCancel();}break;default:break;}}}}
2.3 dialogManager.java的编写
package com.recorderactivity;import android.app.Dialog;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.widget.ImageView;import android.widget.TextView;/** * @ClassName:DialogManager * @Description:TODO * @author :吴雄峰 * @Date : 年5月日 下午8:: * */public class DialogManager {private Dialog dialog;private ImageView mIcon, mVoice;private TextView mLable;@SuppressWarnings("unused")private Context mContext;/*** @Description:TODO* @author :吴雄峰* @Date : 年5月日 下午9::* */public DialogManager(Context context) {this.mContext = context;}public void showRecordingDialog() {dialog = new Dialog(mContext, R.style.Theme_AudioDialog);LayoutInflater mInflater = LayoutInflater.from(mContext);View view = mInflater.inflate(R.layout.dialog_recorder, null);dialog.setContentView(view);mIcon = (ImageView) dialog.findViewById(R.id.id_dialog_recorder_icon);mVoice = (ImageView) dialog.findViewById(R.id.id_dialog_recorder_voice);mLable = (TextView) dialog.findViewById(R.id.id_recorder_dialog_label);dialog.show();}public void 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("手指上滑,取消发送");}}public void 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("松开手指,取消发送");}}public void 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("录音时间过短");}}public void dimissDialog() {if (dialog != null && dialog.isShowing()) {dialog.dismiss();dialog = null;}}/*** * @Description:通过Level去更新图片* @author :吴雄峰**/public void updateVoiceLevel(int level) {if (dialog != null && dialog.isShowing()) {int resId = mContext.getResources().getIdentifier("v" level,"drawable", mContext.getPackageName());mVoice.setImageResource(resId);}}}
2.4 AudioManager.java 的编写
package com.recorderactivity;import java.io.File;import java.io.IOException;import java.util.UUID;import android.media.MediaRecorder;/** * @ClassName:AudioManager * @Description:TODO * @author :吴雄峰 * @Date : 年5月日 下午:: * */public class AudioManager {private MediaRecorder mMediaRecorder;private String mDir;private String mCurrentPath;private static AudioManager mInstance;private String mCurrentFilePath;// 文件路径private boolean isPrepared;public AudioManager(String dir) {mDir = dir;}public interface AudioStateListener {void wellPrepared();}public AudioStateListener mListener;public void setOnAudioStateListener(AudioStateListener listener) {mListener = listener;}public static AudioManager getInstance(String dir) {if (mInstance == null) {synchronized (AudioManager.class) {if (mInstance == null) {mInstance = new AudioManager(dir);}}}return mInstance;}// 准备public void prepareAudio() {try {isPrepared = false;// 创建文件夹File dir = new File(mDir);if (!dir.exists()) {dir.mkdirs();}String fileName = generateFileName();File file = new File(dir, fileName);mCurrentFilePath = file.getAbsolutePath();mMediaRecorder = new MediaRecorder();// 设置输出文件mMediaRecorder.setOutputFile(file.getAbsolutePath());// 设置音频源mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置音频式mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);// 设置音频编码mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);mMediaRecorder.prepare();mMediaRecorder.start();// 准备结束isPrepared = true;if (mListener != null) {mListener.wellPrepared();}} catch (IllegalStateException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}/*** @Description:随机生成文件的名称* @author :吴雄峰* */private String generateFileName() {return UUID.randomUUID().toString() ".amr";}public int getVoiceLevel(int maxLevel) {if (isPrepared) {try {// getMaxAmplitude() 1-return maxLevel * mMediaRecorder.getMaxAmplitude() / 1;} catch (Exception e) {// TODO: handle exception}}return 1;}public void release() {mMediaRecorder.stop();mMediaRecorder.release();mMediaRecorder = null;}public void cancel() {release();if (mCurrentFilePath != null) {File file = new File(mCurrentFilePath);file.delete();mCurrentFilePath = null;}}/*** @Description:* @author :吴雄峰* */public String getCurrentFilePath() {// TODO 自动生成的方法存根return mCurrentFilePath;}}
2.5 MediaManager.java的编写
package com.recorderactivity;import java.io.IOException;import android.media.AudioManager;import android.media.MediaPlayer;import android.media.MediaPlayer.OnCompletionListener;import android.media.MediaPlayer.OnErrorListener;/** * @ClassName:MediaManager * @Description:TODO * @author :吴雄峰 * @Date : 年5月日 下午3:: * */public class MediaManager {private static MediaPlayer mMediaPlayer;private static boolean isPause;public static void playSound(String filePath, OnCompletionListener listener) {if (mMediaPlayer == null) {mMediaPlayer = new MediaPlayer();mMediaPlayer.setOnErrorListener(new OnErrorListener() {@Overridepublic boolean onError(MediaPlayer mp, int what, int extra) {mMediaPlayer.reset();return false;}});} else {mMediaPlayer.reset();}try {mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);mMediaPlayer.setOnCompletionListener(listener);mMediaPlayer.setDataSource(filePath);mMediaPlayer.prepare();mMediaPlayer.start();} catch (IllegalArgumentException e) {// TODO 自动生成的 catch 块e.printStackTrace();} catch (SecurityException e) {// TODO 自动生成的 catch 块e.printStackTrace();} catch (IllegalStateException e) {// TODO 自动生成的 catch 块e.printStackTrace();} catch (IOException e) {// TODO 自动生成的 catch 块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;}}}
2.6 ViewHolder.java 的编写 (封装了,直接贴起来用,不用每次在adapter中写viewholder)
package com.recorderactivity;import android.R.integer;import android.content.Context;import android.graphics.Bitmap;import android.util.SparseArray;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;import android.widget.TextView;/** * @ClassName:Viewholder * @Description:TODO viewholder基类 * @author :吴雄峰 * @Date : 年5月日 下午3:: * */public class Viewholder {private SparseArray<View> mViews;private int mPosition;private View mConvertView;/*** @Description:TODO* @author :吴雄峰* @Date : 年5月日 下午3::* */public Viewholder(Context context, ViewGroup parent, int layoutId,int position) {this.mPosition = position;this.mViews = new SparseArray<View>();mConvertView = LayoutInflater.from(context).inflate(layoutId, parent,false);mConvertView.setTag(this);}/*** * @Description:初始化viewholder* @author :吴雄峰**/public static Viewholder get(Context context, View convertView,ViewGroup parent, int layoutId, int position) {if (convertView == null) {return new Viewholder(context, parent, layoutId, position);} else {Viewholder viewholder = (Viewholder) convertView.getTag();viewholder.mPosition=position;return viewholder;}}/*** * @Description:通过viewID获取控件* @author :吴雄峰**/public <T extends View> T getView(int viewId) {View view = mViews.get(viewId);if (view == null) {view = mConvertView.findViewById(viewId);mViews.put(viewId, view);}return (T) view;}public View getConvertView() {return mConvertView;}/*** * @Description:设置textview 的* @author :吴雄峰**/public Viewholder setText(int viewId,String text){TextView tv=getView(viewId);tv.setText(text);return this;}/*** * @Description:用setImageResource方法设置imageview的图片* @author :吴雄峰**/public Viewholder setImageViewResource(int viewId,int resId){ImageView imageView=getView(viewId);imageView.setImageResource(resId);return this;}/*** * @Description:用setImageBitmap方法设置imageview的图片* @author :吴雄峰**/public Viewholder setImageViewBitmap(int viewId,Bitmap bitmap){ImageView imageView=getView(viewId);imageView.setImageBitmap(bitmap);return this;}public Viewholder setImageViewUri(int viewId,String uri){ImageView imageView=getView(viewId);//imageLoader.getInstance().load(view,uri);return this;}}
2.7 listview 适配器的编写
package com.recorderactivity;import java.util.List;import android.content.Context;import android.util.DisplayMetrics;import android.view.View;import android.view.ViewGroup;import android.view.Window;import android.view.WindowManager;import android.widget.BaseAdapter;/** * @ClassName:ListViewAdapter * @Description:TODO * @author :吴雄峰 * @Date : 年5月日 下午1:: * */public class ListViewAdapter extends BaseAdapter {private int mMinItemWidth;private int mMaxItemWidth;private Context mContext;private List<RecorderEntity> mDatas;public ListViewAdapter(Context context, List<RecorderEntity> data) {// TODO 自动生成的构造函数存根this.mContext = context;this.mDatas = data;WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);DisplayMetrics outMetrics = new DisplayMetrics();wm.getDefaultDisplay().getMetrics(outMetrics);mMaxItemWidth = (int) (outMetrics.widthPixels * 0.7f);mMinItemWidth = (int) (outMetrics.widthPixels * 0.f);}/*** @author :吴雄峰* @Date : 年5月日 下午2::* @Description:*/@Overridepublic int getCount() {// TODO 自动生成的方法存根return mDatas.size();}/*** @author :吴雄峰* @Date : 年5月日 下午2::* @Description:*/@Overridepublic Object getItem(int position) {// TODO 自动生成的方法存根return mDatas.get(position);}/*** @author :吴雄峰* @Date : 年5月日 下午2::* @Description:*/@Overridepublic long getItemId(int position) {// TODO 自动生成的方法存根return position;}/*** @author :吴雄峰* @Date : 年5月日 下午2::* @Description:*/@Overridepublic View getView(int position, View convertView, ViewGroup parent) {Viewholder viewholder = new Viewholder(mContext, parent,R.layout.item_recorder, position);viewholder.setText(R.id.id_recorder_time,Math.round(mDatas.get(position).getmTime()) """);//控制长度View lengh = viewholder.getView(R.id.id_recorder_lengh);ViewGroup.LayoutParams lp = lengh.getLayoutParams();lp.width = (int) (mMinItemWidth mMaxItemWidth / f* mDatas.get(position).getmTime());return viewholder.getConvertView();}}
2.8 listview的实体类编写
package com.recorderactivity;/** * @ClassName:RecorderEntity * @Description:TODO * @author :吴雄峰 * @Date : 年5月日 下午1:: * */public class RecorderEntity {private float mTime;private String mFilePath;/*** @Description:TODO* @author :吴雄峰* @Date : 年5月日 下午1::* */public RecorderEntity(float time, String filePath) {super();this.mFilePath = filePath;this.mTime = time;}/*** @return mTime*/public float getmTime() {return mTime;}/*** @param mTime* 要设置的 mTime*/public void setmTime(float mTime) {this.mTime = mTime;}/*** @return mFilePath*/public String getmFilePath() {return mFilePath;}/*** @param mFilePath* 要设置的 mFilePath*/public void setmFilePath(String mFilePath) {this.mFilePath = mFilePath;}}
dialog的布局编写
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android=" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/dialog_loading_bg" android:gravity="center" android:orientation="vertical" android:padding="dp" > <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" > <ImageView android:id="@id/id_dialog_recorder_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/recorder" android:visibility="visible" /> <ImageView android:id="@id/id_dialog_recorder_voice" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/v1" android:visibility="visible" /> </LinearLayout> <TextView android:id="@id/id_recorder_dialog_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:text="手指上滑,取消发送" android:textColor="#FFFFFF" android:visibility="visible" /></LinearLayout>
listview的item编写
<RelativeLayout xmlns:android=" xmlns:tools=" android:id="@id/RelativeLayout1" android:layout_width="match_parent" android:layout_height="dp" android:layout_marginTop="5dp" > <ImageView android:id="@id/id_icon" android:layout_width="dp" android:layout_height="dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="5dp" android:src="@drawable/icon" /> <FrameLayout android:id="@id/id_recorder_lengh" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_toLeftOf="@id/id_icon" android:background="@drawable/chatto_bg_focused" > <View android:id="@id/id_recorder_anim" android:layout_width="dp" android:layout_height="dp" android:layout_gravity="center_vertical|right" android:background="@drawable/adj" /> </FrameLayout> <TextView android:id="@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/id_recorder_lengh" android:text="" android:textColor="#" /></RelativeLayout>
话说源代码怎么上传。有需要的朋友找我要把,QQ:
Android开发开源项目之-PullToRefresh源码分析 学习Android已经好几年了,但从没有认真写点什么,最近在一位同事的鼓动下有了写博客的想法。这是我第一篇博客,从第三方开源项目开始吧。首先我
android的SDK离线安装详细教程
android中实现自动输入文本效果 此控件的功能是帮助用户实现自动输入,例如当用户输入一个字符后,能够根据这个字符提示显示出与之相关的数据。里面用到了一个适配器来实现此
标签: 仿微信语音聊天
本文链接地址:https://www.jiuchutong.com/biancheng/386089.html 转载请保留说明!上一篇:Android 断点续传的实现(实现断点续传的工具软件)
下一篇:Android开发开源项目之-PullToRefresh源码分析(安卓开源项目叫什么)
友情链接: 武汉网站建设