仿微信6.0语音

图片,分析都是参考慕课网上的Hyman老师的,代码是自己写的。

个人感觉自己的总结还是非常不错的。
效果图:

  1.分析:
          
      RecordingButton三种状态:
               STATE_NORMAL   STATE_RECORDING  STATE_TO_CANCEL

      DialogManager:
          style:      recordingDialog   toCancelDialog  tooShortDialog

     AudioManager:
               prepareAudio();
               cancel() 
               release() --->callBack  toActivity

     
2.伪码编写

          class RecordingButton extends Button{
                    onTouchEvent(event){
                         swich(action){
                              case UP:
                                   if(curState==STATE_RECORDING)
                                    {
                                           
                                            audioManager.cancel();
                                    }
                                   else if(STATE_TO_CANCEL){
                                             audioManager.release();
                                             callBacktoActivity
                                   }
                                   changeState(STATE_NORMAL);
                                   break;
                              case Down:
                                   changeState(STATE_RRCORDING);
                                    longClick--> audioManager.prepare()--->endPrepare()(CallBack)---->DialogManager.showDialog(RecordingDialog)
                                   break;
                              case MOVE:
                                   if(wantToCancel(x,y)){
                                             changeState(STATE_TO_CANCEL)
                                            DialogManager.showDialog(recordingDialog)
                                    }
                                   else
                                    {
                                            changeState(STATE_RECORDING)
                                             DialogManager.showDialog(toCancelDialog)
                                      }
                                   break;
                         }
                    }
          }


3.首先实现自定义按钮,主要是点击时状态之间的切换
          1.fragment_main.xml
          
<? xml version= "1.0" encoding= "utf-8" ?>
<LinearLayout
xmlns: android = "http://schemas.android.com/apk/res/android"
   
android :orientation= "vertical" android :layout_width= "match_parent"
   
android :layout_height= "match_parent"
   
android :background= "@color/mainColor" >
    <ListView
       
android :id= "@+id/main_frag_list"
       
android :layout_width= "match_parent"
       
android :layout_height= "0dp"
       
android :layout_weight= "1" ></ListView>

    <FrameLayout
       
android :layout_width= "match_parent"
       
android :layout_height= "wrap_content"
       
android :background= "@color/white" >
        <com.zhanghao.fangweixinyuyin.widget.RecordingButton
           
android :layout_width= "match_parent"
           
android :layout_height= "40dp"
           
android :layout_gravity= "center"
           
android :text= "@string/state_normal_text"
           
android :textColor= "@color/recordingButtonTextColor"
           
android :textSize= "20sp"
           
android :background= "@drawable/recording_button_bg"
           
android :layout_marginLeft= "40dp"
           
android :layout_marginRight= "40dp"
           
android :layout_marginTop= "10dp"
           
android :layout_marginBottom= "10dp"

        
/>

    </FrameLayout>
</LinearLayout>
   
          2.自定义实现: 
public class RecordingButton extends Button {
   
//设置Button的三个状态
   
private final static int STATE_NORMAL = 1 ;
    private final static  int
STATE_RECORDING = 2 ;
    private final static int
STATE_TO_CANCEL = 3 ;
   
//滑动超过50dp转换到STATE_TO_CANCEL状态
   
private final static float dp = 50 ;
    private int
PIX ;

    private int
curState = 1 ; //设置当前状态

   
public RecordingButton(Context context , AttributeSet attrs) {

       
super (context , attrs) ;
       
PIX =transDpToPix( dp ) ;
   
}

   
@Override
   
public boolean onTouchEvent (MotionEvent event) {
       
float x=event.getX() ;
        float
y=event.getY() ;
        switch
(event.getAction()){
           
case MotionEvent. ACTION_UP :
               
if ( curState == STATE_RECORDING ){

                }
               
else if ( curState == STATE_TO_CANCEL ){

                }
                changeState(
STATE_NORMAL ) ;
                break;
            case
MotionEvent. ACTION_DOWN :
                changeState(
STATE_RECORDING ) ;
                break;
            case
MotionEvent. ACTION_MOVE :
               
if (wantToCancel(x , y)){
                    changeState(
STATE_TO_CANCEL ) ;
               
}
               
else
               
{
                    changeState(
STATE_RECORDING ) ;
               
}
               
break;
       
}
       
return super .onTouchEvent(event) ;
   
}

   
/**
     * 状态改变,改变UI
     *
@param
stateRecording
    
*/
   
private void changeState ( int stateRecording) {
       
switch (stateRecording){
           
case STATE_NORMAL :
                setText(getContext().getResources().getString(R.string.
state_normal_text )) ;
               
setBackgroundResource(R.drawable. recording_button_bg ) ;
               
curState = STATE_NORMAL ;
                break;
            case
STATE_RECORDING :
                setText(getContext().getResources().getString(R.string.
state_recording_text )) ;
               
setBackgroundResource(R.drawable. recording_button_bg_change ) ;
               
curState = STATE_RECORDING ;
                break;
            case
STATE_TO_CANCEL :
                setText(getContext().getResources().getString(R.string.
state_tocancel_text )) ;
               
setBackgroundResource(R.drawable. recording_button_bg_change ) ;
               
curState = STATE_TO_CANCEL ;
                break;
       
}
    }
   
/**
     * 判断是否应该切换到STATE_TO_CANCEL状态
     */
   
public boolean wantToCancel ( float x ,float y){

       
if (x< 0 ||x>getWidth())
           
return true;
        else if
(y<- PIX ||y>getHeight()+ PIX ){
           
return true;

       
}
       
return false;
   
}
   
/**
     * 将dp转换为pix
     */
   
public int transDpToPix ( float dp){
       
float scale=getResources().getDisplayMetrics(). density ;
        return
( int ) (dp*scale+ 0.5f ) ;
   
}

}



4.DialogManager的实现:
          1.dialog的布局文件以及自定义Dialog的实现:
        注意点:
          1.自定义dialog用的是setContentView,但是setContentView(int id)和setContentView(View view)有的时候效果会不同。setContentView(View view),如果这个xml文件用的是relativeLayout布局,大小就会发生变化。

          2.dialog的style
              分别为:
                          1.是否相对于Activity浮动
                          2.设置背景为全透明
                          3.模糊
                          4.半透明
                          5.边框设置

          3.步骤:
                         1.Dialog dialog=new Dialog(context,R.style.recordingStyle);
                         2.dialog.setContentView(view)
                         3.dialog.show();
          
<style name= "recordingDialogStyle" >
    <item
name= "android:windowIsFloating" > true </item>
    <item
name= "android:windowBackground" > @color/transparent </item>
    <item
name= "android:backgroundDimEnabled" > false </item>
    <item
name= "android:windowIsTranslucent" > true </item>
    <item
name= "android:windowFrame" > @null </item>
</style>
               
         
<? xml version= "1.0" encoding= "utf-8" ?>
<LinearLayout
xmlns: android = "http://schemas.android.com/apk/res/android"
   
android :orientation= "vertical" android :layout_width= "wrap_content"
   
android :layout_height= "wrap_content"
   
android :background= "@color/dialog_bg"
   
android :gravity= "center"
   
android :padding= "20dp"
   
>
    <LinearLayout
       
android :layout_width= "wrap_content"
       
android :layout_height= "wrap_content"
       
android :orientation= "horizontal"
       
android :gravity= "center"
       
>
        <ImageView
           
android :id= "@+id/record_record"
           
android :layout_width= "wrap_content"
           
android :layout_height= "wrap_content"
           
android :src= "@mipmap/recorder"
           
android :layout_weight= "1"
         
/>
        <ImageView
           
android :id= "@+id/record_voice"
           
android :layout_width= "wrap_content"
           
android :layout_height= "wrap_content"
           
android :src= "@mipmap/v1"
           
android :layout_weight= "1" />
    </LinearLayout>
    <TextView
       
android :id= "@+id/record_text"
       
android :layout_width= "wrap_content"
       
android :layout_height= "wrap_content"
       
android :text= "@string/recording_dialog_text"
       
android :textColor= "@color/white"
       
android :gravity= "center"
       
android :textSize= "15sp"
       
android :layout_marginTop= "10dp" />
</LinearLayout>



          2.dialogManager中共有6个方法:
                    recordingDialog() //录音对话框
                    recording()  

                    toCancelDialog()  //取消对话框
                    tooShortDialog()  //录音太短对话框
                    dismiss()              //取消对话框
                    updateLevel()      //音量

public class DialogManager {
   
private Dialog dialog ;
    private
Context context ;
    private
View dialogView ;
    private
LayoutInflater inflater ;

    private
ImageView record_Record , record_Voice ;
    private
TextView record_Text ;
    public
DialogManager(Context context) {
       
this . context =context ;
   
}

   
public void recordingDialog (){
       
dialog = new Dialog( context , R.style. recordingDialogStyle ) ;
       
inflater =LayoutInflater.from( context ) ;
       
dialogView = inflater .inflate(R.layout. record_dialog1 ,null ) ;
       
record_Record = (ImageView) dialogView .findViewById(R.id. record_record ) ;
       
record_Voice = (ImageView) dialogView .findViewById(R.id. record_voice ) ;
       
record_Text = (TextView) dialogView .findViewById(R.id. record_text ) ;

       
dialog .setContentView( dialogView ) ;
       
dialog .show() ;
   
}
   
public void recording (){
       
if ( dialog != null && dialog .isShowing()){
           
record_Record .setVisibility(View. VISIBLE ) ;
           
record_Voice .setVisibility(View. VISIBLE ) ;
           
record_Text .setVisibility(View. VISIBLE ) ;

           
record_Record .setImageResource(R.mipmap. recorder ) ;
           
record_Text .setText( context .getResources().getString(R.string. recording_dialog_text )) ;
       
}
    }
   
public void toCancelDialog (){
       
if ( dialog != null && dialog .isShowing()){
           
record_Record .setVisibility(View. VISIBLE ) ;
           
record_Voice .setVisibility(View. INVISIBLE ) ;
           
record_Text .setVisibility(View. VISIBLE ) ;

           
record_Record .setImageResource(R.mipmap. cancel ) ;
           
record_Text .setText( context .getResources().getString(R.string. toCancel_dialog_text )) ;
       
}
    }
   
public void tooShortDialog (){
       
if ( dialog != null && dialog .isShowing()){
           
record_Record .setVisibility(View. VISIBLE ) ;
           
record_Voice .setVisibility(View. INVISIBLE ) ;
           
record_Text .setVisibility(View. VISIBLE ) ;

           
record_Record .setImageResource(R.mipmap. voice_to_short ) ;
           
record_Text .setText( context .getResources().getString(R.string. tooShortDialog_text )) ;
       
}
    }
   
public void dismiss (){
       
if ( dialog != null && dialog .isShowing()){
           
dialog .dismiss() ;
           
dialog = null;
       
}

    }
   
/**
     * 更新音量
     */
   
public void updateLevel ( int level){
       
if ( dialog != null && dialog .isShowing()){
           
int resId= context .getResources().getIdentifier( "v" +level , "mipmap" , context .getPackageName()) ;
           
record_Voice .setImageResource(resId) ;
       
}
    }
}



       3.整合Dialog到RecordingButton
public RecordingButton(Context context , AttributeSet attrs) {

   
super (context , attrs) ;
   
PIX =transDpToPix( dp ) ;
   
manager = new DialogManager(getContext()) ;
   
setOnLongClickListener( new OnLongClickListener() {
       
@Override
       
public boolean onLongClick (View v) {
           
manager .recordingDialog() ;
            return false;
       
}
    })
;
}

@Override
public boolean onTouchEvent (MotionEvent event) {
   
float x=event.getX() ;
    float
y=event.getY() ;
    switch
(event.getAction()){
       
case MotionEvent. ACTION_UP :
           
if ( curState == STATE_RECORDING ){

            }
           
else if ( curState == STATE_TO_CANCEL ){

            }
           
manager .dismiss() ;
           
changeState( STATE_NORMAL ) ;
            break;
        case
MotionEvent. ACTION_DOWN :
            changeState(
STATE_RECORDING ) ;
           
manager .recording() ;
            break;
        case
MotionEvent. ACTION_MOVE :
           
if (wantToCancel(x , y)){
                changeState(
STATE_TO_CANCEL ) ;
               
manager .toCancelDialog() ;
           
}
           
else
           
{
                changeState(
STATE_RECORDING ) ;
               
manager .recording() ;
           
}
           
break;
   
}
    return super .o




5.AudioManager:录制类
          *单例模式
               目的:保证系统中的一个类只有一个实例
               实际做法:将构造方法设置为private,然后设置一个static的get方法去获取类的对象。get方法中加上同步,避免多线程错误。

         
           *回调的实现
                  1.创建一个接口:
                         public interface AudioStateListener{
                                      void wellPrepare();
                          }
                 2.创建一个接口的静态变量
                        public static AudioStateListener mListener;
                 3.创建事件监听方法
                         public void setAudioStateListener(AudioStateListener listener){
                                   mListener=listener;
                          }
                4.然后在需要监听的地方,调用wellPrepared()方法

          
          1.AudioManager代码如下:
public class AudioManager  {
   
private static AudioManager audioManager ;

   
//录音
   
private MediaRecorder mediaRecorder ;
   
//根目录
   
private String dir =Environment. getExternalStorageDirectory()+ "/com.zhanghao.fangweixin" ;
    private boolean
isPrepared = false;
    private
String currentAudioPath ;

    private 
AudioManager() {
    }
   
public static AudioManager getInstance (){
       
if ( audioManager == null ){
           
synchronized (AudioManager. class ){
                   
if ( audioManager == null ){
                       
audioManager = new AudioManager() ;

                   
}
            }
        }
       
return audioManager ;
   
}
   
/**
     * 为audioPrepare()创建回调接口
     *
     */
   
public interface AudioStateListener{
       
void wellPrepared () ;
   
}
   
public static AudioStateListener mlistener ;
    public void
setAudioStateListener (AudioStateListener listener){
       
mlistener =listener ;
   
}
   
/**
     * 准备录音
     */
   
public void audioPrepared (){
        File file=
new File( dir ) ;
        if
(!file.exists())
            file.mkdirs()
;
       
File fileName= new File(file , generateName()) ;

       
mediaRecorder = new MediaRecorder() ;
       
//音频源
       
mediaRecorder .setAudioSource(MediaRecorder.AudioSource. MIC ) ;
       
//输出格式
       
mediaRecorder .setOutputFormat(MediaRecorder.OutputFormat. AMR_NB ) ;
       
//音频编码
       
mediaRecorder .setAudioEncoder(MediaRecorder.AudioEncoder. AMR_NB ) ;
       
//输出路径
       
mediaRecorder .setOutputFile(fileName.getAbsolutePath()) ;
        try
{
           
mediaRecorder .prepare() ;
           
mediaRecorder .start() ;
           
currentAudioPath =fileName.getAbsolutePath() ;
           
isPrepared = true;
       
} catch (IOException e) {
            e.printStackTrace()
;
       
}
       
if ( mlistener != null )
        {
           
mlistener .wellPrepared() ;
       
}

    }
   
/**
     * 随机生成文件名
     */
   
public String generateName (){
       
return UUID.randomUUID().toString()+ ".amr" ;
   
}
   
/**
     * 释放占用的资源
     */
   
public void release (){
       
if ( isPrepared )
           
mediaRecorder .stop() ;
        if
( mediaRecorder != null )
           
mediaRecorder .release() ;
       
mediaRecorder = null;
       
isPrepared = false;
       
currentAudioPath = null;

   
}
   
/**
     * 释放占用资源,并删除生成的文件
     */
   
public void cancel (){
        release()
;
        if
( currentAudioPath != null ){
            File file=
new File( currentAudioPath ) ;
           
file.delete() ;
           
currentAudioPath = null;
       
}
    }
   
/**
     * 获取当前音量等级
     */
   
public int getVoiceLevel ( int maxLevel){
       
if ( isPrepared ){
           
return  ( int ) (maxLevel*( mediaRecorder .getMaxAmplitude()/ 32768.0 )+ 1 ) ;

       
}
       
else
            return
1 ;
   
}
}
               


               2.在AudioRecordingButton中配置AudioManager




6.ListView的编写
          1.在RecordingButton中创建一个回调接口,记录录音正常完成
          2.创建MediaManager类
          3.实现ListView的更新,音乐的播放及动画
public class MainFragment extends Fragment implements RecordingButton.RecorderFinishedListener{
   
private View mainView ;
    private
ListView listView ;
    private
RecordingButton recordingButton ;
    private
ArrayList<AudioBeans> audioBeans = new ArrayList<>() ; //设置数据源
   
private MyAdapter adapter ; //适配器

   
private MediaManager mediaManager ;

    private
View animView ;
   
@Nullable
    @Override
   
public View onCreateView (LayoutInflater inflater , @Nullable ViewGroup container , @Nullable Bundle savedInstanceState) {
       
mainView =inflater.inflate(R.layout. fragment_main , container ,false ) ;
       
init() ;
       
setListener() ;
       
mediaManager =MediaManager.getMediaManager() ;
        return
mainView ;
   
}

   
/**
     * 初始化控件
     */
   
public void init (){
       
listView = (ListView) mainView .findViewById(R.id. main_frag_list ) ;
       
recordingButton = (RecordingButton) mainView .findViewById(R.id. record_button ) ;

       
adapter = new MyAdapter( audioBeans , getContext()) ;
       
listView .setAdapter( adapter ) ;
   
}

   
/**
     * 设置监听
     */
   
public void setListener (){
       
recordingButton .setOnRecorderFinishedListener( this ) ;
       
listView .setOnItemClickListener( new AdapterView.OnItemClickListener() {
           
@Override
           
public void onItemClick (AdapterView<?> parent , View view , int position , long id) {
               
if ( animView != null )
                {
                   
animView .setBackgroundResource(R.mipmap. adj ) ;
                   
animView = null;
               
}
               
mediaManager .prepareMediaPlayer( audioBeans .get(position).getFilePath() , new MediaPlayer.OnCompletionListener() {
                   
@Override
                   
public void onCompletion (MediaPlayer mp) {
                       
animView .setBackgroundResource(R.mipmap. adj ) ;
                   
}
                })
;
               
animView =view.findViewById(R.id. item_bg ) ;
               
animView .setBackgroundResource(R.drawable. play ) ;
               
AnimationDrawable animationDrawable= (AnimationDrawable) animView .getBackground() ;
               
animationDrawable.start() ;
           
}
        })
;

   
}

   
@Override
   
public void recorderFinish ( float seconds , String filePath) {
        AudioBeans audioBean=
new AudioBeans() ;
       
audioBean.setSeconds(seconds) ;
       
audioBean.setFilePath(filePath) ;
       
audioBeans .add(audioBean) ;
       
adapter .notifyDataSetChanged() ;
   
}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值