仿微信语音聊天

最近项目中做了一个聊天的功能,为了和Web端的配合,用WebSocket实现了文字聊天和单张图片的发送聊天,参见:http://blog.csdn.net/lijinweii/article/details/73551370  ,但是在实现语音聊天的时候发了难里了,苦思无果,果断在网上查了下找到了篇“仿微信语音聊天”的功能。看了以后顿时心花怒放,这不就是我所需要吗。。。

哈哈哈,看下效果图啊:

他的功能和微信语音聊天及其相似。

看下代码:

AudioManager.java:

public class AudioManager {

    private MediaRecorder mRecorder;
    private String mDirString;
    private String mCurrentFilePathString;

    private boolean isPrepared;// 是否准备好了

    /**
     * 单例化的方法 1 先声明一个static 类型的变量a 2 在声明默认的构造函数 3 再用public synchronized static
     * 类名 getInstance() { if(a==null) { a=new 类();} return a; } 或者用以下的方法
     */

    /**
     * 单例化这个类
     */
    private static AudioManager mInstance;

    private AudioManager(String dir) {
        mDirString = dir;
    }

    public static AudioManager getInstance(String dir) {
        if (mInstance == null) {
            synchronized (AudioManager.class) {
                if (mInstance == null) {
                    mInstance = new AudioManager(dir);

                }
            }
        }
        return mInstance;
    }

    /**
     * 回调函数,准备完毕,准备好后,button才会开始显示录音框
     * @author nickming
     */
    public interface AudioStageListener {
        void wellPrepared();
    }

    public AudioStageListener mListener;

    public void setOnAudioStageListener(AudioStageListener listener) {
        mListener = listener;
    }

    // 准备方法
    public void prepareAudio() {

        try {
            // 一开始应该是false的
            isPrepared = false;

//            Public File(String path)
//            参数为String,构造一个新的File使用指定的路径
            File dir = new File(mDirString);
            if (!dir.exists()) {
               /* mkdir():只能创建一层目录.
                如: File file = new File("c:/aa");file.mkdir(); //这样可以在c:/下创建aa目录
                假如有多层目录,则不能用mkdir创建:File file = new File("c:/aa/bb"); file.mkdir() //这样创建不了.
                应该改为:mkdirs():*/
                dir.mkdirs();
            }

            //随机生成文件的名称
            String fileNameString = generalFileName();
//            public File(File dir,String name)
//            参数为File和String,File指定构造的新的File对象的路径(文件路径),而String指定新的File名字(文件名称)
            File file = new File(dir, fileNameString);

            mCurrentFilePathString = file.getAbsolutePath();//获取录音文件的路径

            mRecorder = new MediaRecorder();
            // 设置输出文件
            mRecorder.setOutputFile(file.getAbsolutePath());
            // 设置meidaRecorder的音频源是麦克风
            mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            // 设置文件音频的输出格式为amr
            mRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
            // 设置音频的编码格式为amr
            mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

            // 严格遵守google官方api给出的mediaRecorder的状态流程图
            mRecorder.prepare();

            mRecorder.start();
            // 准备结束
            isPrepared = true;
            // 已经准备好了,可以录制了
            if (mListener != null) {
                mListener.wellPrepared();
            }
        } catch (IllegalStateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


    /**
     * 随机生成文件的名称
     *
     * @return
     */
    private String generalFileName() {
        // TODO Auto-generated method stub
        return UUID.randomUUID().toString() + ".amr";
    }


    // 获得声音的level
    public int getVoiceLevel(int maxLevel) {
        // mRecorder.getMaxAmplitude()这个是音频的振幅范围,值域是1-32767
        if (isPrepared) {
            try {
                // 取证+1,否则去不到7
                return maxLevel * mRecorder.getMaxAmplitude() / 32768 + 1;
            } catch (Exception e) {
                // TODO Auto-generated catch block
            }
        }
        return 1;
    }


    // 释放资源
    public void release() {
        // 严格按照api流程进行
        mRecorder.stop();
        mRecorder.release();
        mRecorder = null;

    }


    // 取消,因为prepare时产生了一个文件,所以cancel方法应该要删除这个文件,
    // 这是与release的方法的区别
    public void cancel() {
        release();
        if (mCurrentFilePathString != null) {
            File file = new File(mCurrentFilePathString);
            file.delete();
            mCurrentFilePathString = null;
        }
    }

    //录音的文件路径
    public String getCurrentFilePath() {
        // TODO Auto-generated method stub
        return mCurrentFilePathString;
    }

}


AudioRecorderButton.java:

public class AudioRecordButton extends Button implements AudioManager.AudioStageListener {

    private static final int STATE_NORMAL = 1;// 默认的状态
    private static final int STATE_RECORDING = 2;// 正在录音
    private static final int STATE_WANT_TO_CANCEL = 3;// 希望取消

    private static final int DISTANCE_Y_CANCEL = 50;

    private int mCurrentState = STATE_NORMAL;// 当前的状态

    private boolean isRecording = false; // 已经开始录音

    private DialogManager mDialogManager;

    private AudioManager mAudioManager;

    private float mTime = 0;
    // 是否触发了onlongclick,准备好了
    private boolean mReady;

    /**
     * 先实现两个参数的构造方法,布局会默认引用这个构造方法, 用一个 构造参数的构造方法来引用这个方法 * @param context
     */

    public AudioRecordButton(Context context) {
        this(context, null);
        // TODO Auto-generated constructor stub
    }

    public AudioRecordButton(final Context context, AttributeSet attrs) {
        super(context, attrs);

        mDialogManager = new DialogManager(getContext());
        // 这里没有判断储存卡是否存在,有空要判断
        String dir = Environment.getExternalStorageDirectory() + "/nickming_recorder_audios";
        mAudioManager = AudioManager.getInstance(dir);
        mAudioManager.setOnAudioStageListener(this);

        // 由于这个类是button所以在构造方法中添加监听事件
        setOnLongClickListener(new OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                // TODO Auto-generated method
                mReady = true;

                // 准备方法
                mAudioManager.prepareAudio();

                return false;
            }
        });
        // TODO Auto-generated constructor stub
    }

    /**
     * 录音完成后的回调,回调给activiy,可以获得mtime和文件的路径
     *
     * @author nickming
     */
    public interface AudioFinishRecorderListener {
        void onFinished(float seconds, String filePath);
    }

    private AudioFinishRecorderListener mListener;

    public void setAudioFinishRecorderListener(AudioFinishRecorderListener listener) {
        mListener = listener;
    }

    // 获取音量大小的runnable
    private Runnable mGetVoiceLevelRunnable = new Runnable() {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (isRecording) {// 开始录音
                try {
                    Thread.sleep(100);
                    mTime += 0.1f;
                    mhandler.sendEmptyMessage(MSG_VOICE_CHANGE);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    };

    // 准备三个常量
    private static final int MSG_AUDIO_PREPARED = 0X110;
    private static final int MSG_VOICE_CHANGE = 0X111;
    private static final int MSG_DIALOG_DIMISS = 0X112;

    private Handler mhandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_AUDIO_PREPARED:
                    // 显示应该是在audio end prepare之后回调  显示對話框在开始录音以后
                    mDialogManager.showRecordingDialog();// 显示录音的对话框
                    isRecording = true;
                    new Thread(mGetVoiceLevelRunnable).start();//开启一个线程
                    // 需要开启一个线程来变换音量
                    break;
                case MSG_VOICE_CHANGE:
                    // 显示更新音量级别的对话框
                    mDialogManager.updateVoiceLevel(mAudioManager.getVoiceLevel(7));

                    break;
                case MSG_DIALOG_DIMISS:

                    break;

            }
        }

        ;
    };

    // 在这里面发送一个handler的消息
    @Override
    public void wellPrepared() {
        // TODO Auto-generated method stub
        mhandler.sendEmptyMessage(MSG_AUDIO_PREPARED);
    }

    /**
     * 直接复写这个监听函数
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        int action = event.getAction();
        int x = (int) event.getX();// 获得x轴坐标
        int y = (int) event.getY();// 获得y轴坐标

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                changeState(STATE_RECORDING);
                break;
            case MotionEvent.ACTION_MOVE:

                if (isRecording) {

                    // 根据x,y来判断用户是否想要取消
                    if (wantToCancel(x, y)) {
                        changeState(STATE_WANT_TO_CANCEL);
                    } else {
                        changeState(STATE_RECORDING);
                    }
                }

                break;
            case MotionEvent.ACTION_UP:
                // 首先判断是否有触发onlongclick事件,没有的话直接返回reset
                if (!mReady) {
                    reset();
                    return super.onTouchEvent(event);
                }
                // 如果按的时间太短,还没准备好或者时间录制太短,就离开了,则显示这个dialog
                if (!isRecording || mTime < 0.6f) {
                    mDialogManager.tooShort();
                    mAudioManager.cancel();
                    mhandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS, 1300);// 持续1.3s
                } else if (mCurrentState == STATE_RECORDING) {//正常录制结束

                    mDialogManager.dimissDialog();

                    mAudioManager.release();// release释放一个mediarecorder

                    if (mListener != null) {// 并且callbackActivity,保存录音

                        mListener.onFinished(mTime, mAudioManager.getCurrentFilePath());//录音多久(时间段), 文件路径
                    }
                } else if (mCurrentState == STATE_WANT_TO_CANCEL) {// 想要取消
                    // cancel
                    mAudioManager.cancel();
                    mDialogManager.dimissDialog();
                }
                reset();// 恢复标志位

                break;
        }
        return super.onTouchEvent(event);
    }

    /**
     * 回复标志位以及状态
     */
    private void reset() {
        // TODO Auto-generated method stub
        isRecording = false;
        changeState(STATE_NORMAL);// 默认的状态
        mReady = false;
        mTime = 0;
    }

    private boolean wantToCancel(int x, int y) {
        // TODO Auto-generated method stub

        if (x < 0 || x > getWidth()) {// 判断是否在左边,右边,上边,下边  // 超过按钮的宽度
            return true;
        }
        if (y < -DISTANCE_Y_CANCEL || y > getHeight() + DISTANCE_Y_CANCEL) {  // 超过按钮的高度
            return true;
        }

        return false;
    }

    /**
     * 改变
     */
    private void changeState(int state) {
        // TODO Auto-generated method stub
        if (mCurrentState != state) {
            mCurrentState = state;
            switch (mCurrentState) {
                case STATE_NORMAL:// 默认的状态
                    setBackgroundResource(R.drawable.button_recordnormal);
                    setText(R.string.normal);

                    break;
                case STATE_RECORDING:// 正在录音
                    setBackgroundResource(R.drawable.button_recording);
                    setText(R.string.recording);
                    if (isRecording) {
                        mDialogManager.recording();
                        // 复写dialog.recording();
                    }
                    break;

                case STATE_WANT_TO_CANCEL://希望取消
                    setBackgroundResource(R.drawable.button_recording);
                    setText(R.string.want_to_cancle);
                    // dialog want to cancel
                    mDialogManager.wantToCancel();
                    break;
            }
        }
    }

    @Override
    public boolean onPreDraw() {
        // TODO Auto-generated method stub
        return false;
    }
    
}

DialogManager.java:

//用于管理Dialog
public class DialogManager {

	/**
	 * 以下为dialog的初始化控件,包括其中的布局文件
	 */

	private Dialog mDialog;

	private ImageView mIcon;
	private ImageView mVoice;

	private TextView mLable;

	private Context mContext;
	/**
	 * 构造方法 传入上下文
	 */
	public DialogManager(Context context) {
		// TODO Auto-generated constructor stub
		mContext = context;
	}
	// 显示录音的对话框
	public void showRecordingDialog() {
		// TODO Auto-generated method stub

		mDialog = new Dialog(mContext,R.style.Theme_audioDialog);
		// 用layoutinflater来引用布局
		LayoutInflater inflater = LayoutInflater.from(mContext);
		View view = inflater.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);
		mLable = (TextView) mDialog.findViewById(R.id.recorder_dialogtext);
		mDialog.show();
		
	}

	/**
	 * 设置正在录音时的dialog界面
	 */
	public void recording() {
		if (mDialog != null && mDialog.isShowing()) {//显示状态
			mIcon.setVisibility(View.VISIBLE);
			mVoice.setVisibility(View.VISIBLE);
			mLable.setVisibility(View.VISIBLE);

			mIcon.setImageResource(R.drawable.recorder);
			mLable.setText(R.string.shouzhishanghua);
		}
	}

	/**
	 * 取消界面
	 */
	public void wantToCancel() {  // 显示想取消的对话框
		// TODO Auto-generated method stub
		if (mDialog != null && mDialog.isShowing()) {//显示状态
			mIcon.setVisibility(View.VISIBLE);
			mVoice.setVisibility(View.GONE);
			mLable.setVisibility(View.VISIBLE);

			mIcon.setImageResource(R.drawable.cancel);
			mLable.setText(R.string.want_to_cancle);
		}

	}

	// 时间过短
	public void tooShort() {  // 显示时间过短的对话框
		// TODO Auto-generated method stub
		if (mDialog != null && mDialog.isShowing()) {
			mIcon.setVisibility(View.VISIBLE);
			mVoice.setVisibility(View.GONE);
			mLable.setVisibility(View.VISIBLE);

			mIcon.setImageResource(R.drawable.voice_to_short);
			mLable.setText(R.string.tooshort);
		}

	}

	// 隐藏dialog
	public void dimissDialog() {
		// 显示取消的对话框
		// TODO Auto-generated method stub
		if (mDialog != null && mDialog.isShowing()) {
			mDialog.dismiss();
			mDialog = null;
		}
	}

	// 显示更新音量级别的对话框
	public void updateVoiceLevel(int level) {
		// TODO Auto-generated method stub

		if (mDialog != null && mDialog.isShowing()) {

			//先不改变它的默认状态
//			mIcon.setVisibility(View.VISIBLE);
//			mVoice.setVisibility(View.VISIBLE);
//			mLable.setVisibility(View.VISIBLE);

			//通过level来找到图片的id,也可以用switch来寻址,但是代码可能会比较长
			int resId = mContext.getResources().getIdentifier("v" + level,
					"drawable", mContext.getPackageName());

			//设置图片的id
			mVoice.setImageResource(resId);
		}

	}

}

MediaManager.java

public class MediaManager {
	
	private static MediaPlayer mPlayer;
	
	private static boolean isPause;

	public static  void playSound(String filePathString, OnCompletionListener onCompletionListener) {
		// TODO Auto-generated method stub
		if (mPlayer==null) {
			mPlayer=new MediaPlayer();
			//保险起见,设置报错监听 //设置一个error监听器
			mPlayer.setOnErrorListener(new OnErrorListener() {
				
				@Override
				public boolean onError(MediaPlayer mp, int what, int extra) {
					// TODO Auto-generated method stub
					mPlayer.reset();
					return false;
				}
			});
		}else {
			mPlayer.reset();//就回复
		}
		
		try {
			mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
			mPlayer.setOnCompletionListener(onCompletionListener);
			mPlayer.setDataSource(filePathString);
			mPlayer.prepare();
			mPlayer.start();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalStateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	//停止函数
	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;
		}
	}
}

RecoderAdapter.java

public class RecorderAdapter extends ArrayAdapter<MainActivity.Recorder> {


	private LayoutInflater inflater;

	private int mMinItemWith;// 设置对话框的最大宽度和最小宽度  //最小的item宽度
	private int mMaxItemWith; //最大的item宽度

	public RecorderAdapter(Context context, List<MainActivity.Recorder> dataList) {
		super(context, -1, dataList);
		// TODO Auto-generated constructor stub
		inflater = LayoutInflater.from(context);

		// 获取系统宽度
		WindowManager wManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
		DisplayMetrics outMetrics = new DisplayMetrics();
		wManager.getDefaultDisplay().getMetrics(outMetrics);
		mMaxItemWith = (int) (outMetrics.widthPixels * 0.7f);
		mMinItemWith = (int) (outMetrics.widthPixels * 0.15f);
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder viewHolder = null;
		if (convertView == null) {
			convertView = inflater.inflate(R.layout.item_layout, parent, false);
			
			viewHolder=new ViewHolder();
			viewHolder.seconds=(TextView) convertView.findViewById(R.id.recorder_time);
			viewHolder.length=convertView.findViewById(R.id.recorder_length);
			
			convertView.setTag(viewHolder);
		}else {
			viewHolder=(ViewHolder) convertView.getTag();
		}

		viewHolder.seconds.setText(Math.round(getItem(position).time)+"\"");
		ViewGroup.LayoutParams lParams=viewHolder.length.getLayoutParams();
		lParams.width=(int) (mMinItemWith+mMaxItemWith/60f*getItem(position).time);
		viewHolder.length.setLayoutParams(lParams);
		
		return convertView;
	}

	/**
	 * 定义一个ViewHolder
	 */
	class ViewHolder {
		TextView seconds;// 时间
		View length;// 对话框长度
	}

}

如果大家感兴趣去搜索的话,会发现到现在为止代码和网上搜索的基本一样,那我们看了干嘛,别急,不是要有一个Main类吗?

如果大家再往上找到这样的一个防微信语音聊天的Demo 然后导入自己项目会发现报错:


查看后才发现是因为权限的问题,那我们就加上权限:

 <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

再运行会发现还是报这个错误,为什么呢?

大家都知道Android6.0 以后,对于一些危险权限的添加需要去动态添加。这里“录音”,“SD卡”的权限都属于危险权限,所以我们要动态添加权限才可以,我们看下MainActivity。java类:

public class MainActivity extends BaseActivity {

    AudioRecordButton button;

    private ListView mlistview;
    private ArrayAdapter<Recorder> mAdapter;
    private View viewanim;
    private List<Recorder> mDatas = new ArrayList<Recorder>();

    @Override
    public int getLayoutResId() {
        return R.layout.activity_main;
    }


    @Override
    protected void initView() {
        mlistview = (ListView) findViewById(R.id.listview);
        button = (AudioRecordButton) findViewById(R.id.recordButton);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//SDK>=23 编译版本 > 23
            //checkSelfPermission用来检测应用是否已经具有权限
            if (!(ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED)) {
                if (ActivityCompat.shouldShowRequestPermissionRationale(activity,Manifest.permission.RECORD_AUDIO)) {
                    //去设置权限(申请权限) dialog
                    MySystemUtils.goToSetPermission(activity, getResources().getString(R.string.permission_record_audio), JHConstants.RECORD_AUDIO);
                } else {
                    //进行请求单个或多个权限
                    ActivityCompat.requestPermissions(activity,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, JHConstants.RECORD_AUDIO);
                }
            } else {
                Log.i("--", "onClick granted");
                button.setAudioFinishRecorderListener(new AudioRecordButton.AudioFinishRecorderListener() {
                    @Override
                    public void onFinished(float seconds, String filePath) {
                        // TODO Auto-generated method stub
                        Recorder recorder = new Recorder(seconds, filePath);
                        mDatas.add(recorder);
                        mAdapter.notifyDataSetChanged();
                        mlistview.setSelection(mDatas.size() - 1);
                    }
                });
            }
        } else {

            button.setAudioFinishRecorderListener(new AudioRecordButton.AudioFinishRecorderListener() {
                @Override
                public void onFinished(float seconds, String filePath) {
                    // TODO Auto-generated method stub
                    Recorder recorder = new Recorder(seconds, filePath);
                    mDatas.add(recorder);
                    mAdapter.notifyDataSetChanged();
                    mlistview.setSelection(mDatas.size() - 1);
                }
            });
        }
    }

    @Override
    protected void initListener() {

/*        button.setAudioFinishRecorderListener(new AudioRecordButton.AudioFinishRecorderListener() {

            @Override
            public void onFinished(float seconds, String filePath) {
                // TODO Auto-generated method stub
                Recorder recorder = new Recorder(seconds, filePath);
                mDatas.add(recorder);
                mAdapter.notifyDataSetChanged();
                mlistview.setSelection(mDatas.size() - 1);
            }
        });*/

        mAdapter = new RecorderAdapter(this, mDatas);
        mlistview.setAdapter(mAdapter);
        mlistview.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                // TODO Auto-generated method stub
                // 播放动画
                if (viewanim != null) {//让第二个播放的时候第一个停止播放
                    viewanim.setBackgroundResource(R.drawable.adj);
                    viewanim = null;
                }
                viewanim = view.findViewById(R.id.id_recorder_anim);
                viewanim.setBackgroundResource(R.drawable.play);
                AnimationDrawable drawable = (AnimationDrawable) viewanim.getBackground();
                drawable.start();

                // 播放音频
                MediaManager.playSound(mDatas.get(position).filePathString, new MediaPlayer.OnCompletionListener() {

                    @Override
                    public void onCompletion(MediaPlayer mp) {
                        viewanim.setBackgroundResource(R.drawable.adj);

                    }
                });
            }
        });

    }

    @Override
    protected void initData() {
    }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        super.onPause();
        MediaManager.pause();
    }

    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();
        MediaManager.resume();
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        MediaManager.release();
    }

    public class Recorder {
        float time;
        String filePathString;

        public Recorder(float time, String filePathString) {
            super();
            this.time = time;
            this.filePathString = filePathString;
        }

        public float getTime() {
            return time;
        }

        public void setTime(float time) {
            this.time = time;
        }

        public String getFilePathString() {
            return filePathString;
        }

        public void setFilePathString(String filePathString) {
            this.filePathString = filePathString;
        }
    }

    @Override//权限请求结果 用户对请求作出响应后的回调
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == JHConstants.RECORD_AUDIO) {
            Boolean grantResultBoolean = false;
            for (int grantResult : grantResults) {
                grantResultBoolean = (grantResult == PackageManager.PERMISSION_GRANTED);
                if (!grantResultBoolean) {
                    break;
                }
            }
            if (grantResultBoolean) {
                //通过
                button.setAudioFinishRecorderListener(new AudioRecordButton.AudioFinishRecorderListener() {
                    @Override
                    public void onFinished(float seconds, String filePath) {
                        // TODO Auto-generated method stub
                        Recorder recorder = new Recorder(seconds, filePath);
                        mDatas.add(recorder);
                        mAdapter.notifyDataSetChanged();
                        mlistview.setSelection(mDatas.size() - 1);
                    }
                });

            } else {
                MySystemUtils.goToSetPermission(activity, getResources().getString(R.string.permission_record_audio_and_sd), JHConstants.RECORD_AUDIO);
            }
        }
    }
    
}

这样就可以正常运行了。

当然有的同志在测试的时候用的是模拟器:发现还是报错误



如果你去查找一下会发现:




好了,这里我就不多说什么了,记得要在真机上运行才可以。

源码地址:http://download.csdn.net/detail/lijinweii/9881928

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值