欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

Android仿微信語音對(duì)講錄音功能

自微信出現(xiàn)以來取得了很好的成績,語音對(duì)講的實(shí)現(xiàn)更加方便了人與人之間的交流。今天來實(shí)踐一下微信的語音對(duì)講的錄音實(shí)現(xiàn),這個(gè)也比較容易實(shí)現(xiàn)。在此,我將該按鈕封裝成為一個(gè)控件,并通過策略模式的方式實(shí)現(xiàn)錄音和界面的解耦合,以方便我們?cè)趯?shí)際情況中對(duì)錄音方法的不同需求(例如想要實(shí)現(xiàn)wav格式的編碼時(shí)我們也就不能再使用MediaRecorder,而只能使用AudioRecord進(jìn)行處理)。

成都創(chuàng)新互聯(lián)公司專注于企業(yè)網(wǎng)絡(luò)營銷推廣、網(wǎng)站重做改版、博白網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5開發(fā)、商城建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為博白等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。

效果圖:

Android仿微信語音對(duì)講錄音功能

實(shí)現(xiàn)思路:

1.在微信中我們可以看到實(shí)現(xiàn)語音對(duì)講的是通過點(diǎn)按按鈕來完成的,因此在這里我選擇重新自己的控件使其繼承自Button并重寫onTouchEvent方法,來實(shí)現(xiàn)對(duì)錄音的判斷。

2.在onTouchEvent方法中,

當(dāng)我們按下按鈕時(shí),首先顯示錄音的對(duì)話框,然后調(diào)用錄音準(zhǔn)備方法并開始錄音,接著開啟一個(gè)計(jì)時(shí)線程,每隔0.1秒的時(shí)間獲取一次錄音音量的大小,并通過Handler根據(jù)音量大小更新Dialog中的顯示圖片;

當(dāng)我們移動(dòng)手指時(shí),若手指向上移動(dòng)距離大于50,在Dialog中顯示松開手指取消錄音的提示,并將isCanceled變量(表示我們最后是否取消了錄音)置為true,上移動(dòng)距離小于20時(shí),我們恢復(fù)Dialog的圖片,并將isCanceled置為false;
當(dāng)抬起手指時(shí),我們首先關(guān)閉錄音對(duì)話框,接著調(diào)用錄音停止方法并關(guān)閉計(jì)時(shí)線程,然后我們判斷是否取消錄音,若是的話則刪除錄音文件,否則判斷計(jì)時(shí)時(shí)間是否太短,最后調(diào)用回調(diào)接口中的recordEnd方法。

3.在這里為了適應(yīng)不同的錄音需求,我使用了策略模式來進(jìn)行處理,將每一個(gè)不同的錄音方法視為一種不同的策略,根據(jù)自己的需要去改寫。

注意問題

1.在onTouchEvent的返回值中應(yīng)該返回true,這樣才能屏蔽之后其他的觸摸事件,否則當(dāng)手指滑動(dòng)離開Button之后將不能在響應(yīng)我們的觸摸方法。
2.不要忘記為自己的App添加權(quán)限:

<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" />

代碼參考

RecordButton 類,我們的自定義控件,重新復(fù)寫了onTouchEvent方法

package com.example.recordtest;

import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class RecordButton extends Button {

  private static final int MIN_RECORD_TIME = 1; // 最短錄音時(shí)間,單位秒
  private static final int RECORD_OFF = 0; // 不在錄音
  private static final int RECORD_ON = 1; // 正在錄音

  private Dialog mRecordDialog;
  private RecordStrategy mAudioRecorder;
  private Thread mRecordThread;
  private RecordListener listener;

  private int recordState = 0; // 錄音狀態(tài)
  private float recodeTime = 0.0f; // 錄音時(shí)長,如果錄音時(shí)間太短則錄音失敗
  private double voiceValue = 0.0; // 錄音的音量值
  private boolean isCanceled = false; // 是否取消錄音
  private float downY;

  private TextView dialogTextView;
  private ImageView dialogImg;
  private Context mContext;

  public RecordButton(Context context) {
    super(context);
    // TODO Auto-generated constructor stub
    init(context);
  }

  public RecordButton(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    // TODO Auto-generated constructor stub
    init(context);
  }

  public RecordButton(Context context, AttributeSet attrs) {
    super(context, attrs);
    // TODO Auto-generated constructor stub
    init(context);
  }

  private void init(Context context) {
    mContext = context;
    this.setText("按住 說話");
  }

  public void setAudioRecord(RecordStrategy record) {
    this.mAudioRecorder = record;
  }

  public void setRecordListener(RecordListener listener) {
    this.listener = listener;
  }

  // 錄音時(shí)顯示Dialog
  private void showVoiceDialog(int flag) {
    if (mRecordDialog == null) {
      mRecordDialog = new Dialog(mContext, R.style.Dialogstyle);
      mRecordDialog.setContentView(R.layout.dialog_record);
      dialogImg = (ImageView) mRecordDialog
          .findViewById(R.id.record_dialog_img);
      dialogTextView = (TextView) mRecordDialog
          .findViewById(R.id.record_dialog_txt);
    }
    switch (flag) {
    case 1:
      dialogImg.setImageResource(R.drawable.record_cancel);
      dialogTextView.setText("松開手指可取消錄音");
      this.setText("松開手指 取消錄音");
      break;

    default:
      dialogImg.setImageResource(R.drawable.record_animate_01);
      dialogTextView.setText("向上滑動(dòng)可取消錄音");
      this.setText("松開手指 完成錄音");
      break;
    }
    dialogTextView.setTextSize(14);
    mRecordDialog.show();
  }

  // 錄音時(shí)間太短時(shí)Toast顯示
  private void showWarnToast(String toastText) {
    Toast toast = new Toast(mContext);
    View warnView = LayoutInflater.from(mContext).inflate(
        R.layout.toast_warn, null);
    toast.setView(warnView);
    toast.setGravity(Gravity.CENTER, 0, 0);// 起點(diǎn)位置為中間
    toast.show();
  }

  // 開啟錄音計(jì)時(shí)線程
  private void callRecordTimeThread() {
    mRecordThread = new Thread(recordThread);
    mRecordThread.start();
  }

  // 錄音Dialog圖片隨錄音音量大小切換
  private void setDialogImage() {
    if (voiceValue < 600.0) {
      dialogImg.setImageResource(R.drawable.record_animate_01);
    } else if (voiceValue > 600.0 && voiceValue < 1000.0) {
      dialogImg.setImageResource(R.drawable.record_animate_02);
    } else if (voiceValue > 1000.0 && voiceValue < 1200.0) {
      dialogImg.setImageResource(R.drawable.record_animate_03);
    } else if (voiceValue > 1200.0 && voiceValue < 1400.0) {
      dialogImg.setImageResource(R.drawable.record_animate_04);
    } else if (voiceValue > 1400.0 && voiceValue < 1600.0) {
      dialogImg.setImageResource(R.drawable.record_animate_05);
    } else if (voiceValue > 1600.0 && voiceValue < 1800.0) {
      dialogImg.setImageResource(R.drawable.record_animate_06);
    } else if (voiceValue > 1800.0 && voiceValue < 2000.0) {
      dialogImg.setImageResource(R.drawable.record_animate_07);
    } else if (voiceValue > 2000.0 && voiceValue < 3000.0) {
      dialogImg.setImageResource(R.drawable.record_animate_08);
    } else if (voiceValue > 3000.0 && voiceValue < 4000.0) {
      dialogImg.setImageResource(R.drawable.record_animate_09);
    } else if (voiceValue > 4000.0 && voiceValue < 6000.0) {
      dialogImg.setImageResource(R.drawable.record_animate_10);
    } else if (voiceValue > 6000.0 && voiceValue < 8000.0) {
      dialogImg.setImageResource(R.drawable.record_animate_11);
    } else if (voiceValue > 8000.0 && voiceValue < 10000.0) {
      dialogImg.setImageResource(R.drawable.record_animate_12);
    } else if (voiceValue > 10000.0 && voiceValue < 12000.0) {
      dialogImg.setImageResource(R.drawable.record_animate_13);
    } else if (voiceValue > 12000.0) {
      dialogImg.setImageResource(R.drawable.record_animate_14);
    }
  }

  // 錄音線程
  private Runnable recordThread = new Runnable() {

    @Override
    public void run() {
      recodeTime = 0.0f;
      while (recordState == RECORD_ON) {
        {
          try {
            Thread.sleep(100);
            recodeTime += 0.1;
            // 獲取音量,更新dialog
            if (!isCanceled) {
              voiceValue = mAudioRecorder.getAmplitude();
              recordHandler.sendEmptyMessage(1);
            }
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }
  };

  @SuppressLint("HandlerLeak")
  private Handler recordHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      setDialogImage();
    }
  };

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    // TODO Auto-generated method stub
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN: // 按下按鈕
      if (recordState != RECORD_ON) {
        showVoiceDialog(0);
        downY = event.getY();
        if (mAudioRecorder != null) {
          mAudioRecorder.ready();
          recordState = RECORD_ON;
          mAudioRecorder.start();
          callRecordTimeThread();
        }
      }
      break;
    case MotionEvent.ACTION_MOVE: // 滑動(dòng)手指
      float moveY = event.getY();
      if (downY - moveY > 50) {
        isCanceled = true;
        showVoiceDialog(1);
      }
      if (downY - moveY < 20) {
        isCanceled = false;
        showVoiceDialog(0);
      }
      break;
    case MotionEvent.ACTION_UP: // 松開手指
      if (recordState == RECORD_ON) {
        recordState = RECORD_OFF;
        if (mRecordDialog.isShowing()) {
          mRecordDialog.dismiss();
        }
        mAudioRecorder.stop();
        mRecordThread.interrupt();
        voiceValue = 0.0;
        if (isCanceled) {
          mAudioRecorder.deleteOldFile();
        } else {
          if (recodeTime < MIN_RECORD_TIME) {
            showWarnToast("時(shí)間太短 錄音失敗");
            mAudioRecorder.deleteOldFile();
          } else {
            if (listener != null) {
              listener.recordEnd(mAudioRecorder.getFilePath());
            }
          }
        }
        isCanceled = false;
        this.setText("按住 說話");
      }
      break;
    }
    return true;
  }

  public interface RecordListener {
    public void recordEnd(String filePath);
  }
}

Dialog布局:

<?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:layout_gravity="center"
  android:gravity="center"
  android:background="@drawable/record_bg"  
  android:padding="20dp" >

  <ImageView
    android:id="@+id/record_dialog_img"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

  <TextView
    android:id="@+id/record_dialog_txt"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textColor="@android:color/white"
    android:layout_marginTop="5dp" />

</LinearLayout>

錄音時(shí)間太短的Toast布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:background="@drawable/record_bg"
  android:padding="20dp"
  android:gravity="center"
  android:orientation="vertical" >

  <ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/voice_to_short" />

  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textColor="@android:color/white"
    android:textSize="15sp"
    android:text="時(shí)間太短 錄音失敗" />

</LinearLayout>

自定義的Dialogstyle,對(duì)話框樣式

<style name="Dialogstyle">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowFrame">@null</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowIsFloating">true</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
    <!-- 顯示對(duì)話框時(shí)當(dāng)前的屏幕是否變暗 -->
    <item name="android:backgroundDimEnabled">false</item>
</style>

RecordStrategy 錄音策略接口

package com.example.recordtest;

/**
 * RecordStrategy 錄音策略接口
 * @author acer
 */
public interface RecordStrategy {

  /**
   * 在這里進(jìn)行錄音準(zhǔn)備工作,重置錄音文件名等
   */
  public void ready();
  /**
   * 開始錄音
   */
  public void start();
  /**
   * 錄音結(jié)束
   */
  public void stop();

  /**
   * 錄音失敗時(shí)刪除原來的舊文件
   */
  public void deleteOldFile();

  /**
   * 獲取錄音音量的大小
   * @return 
   */
  public double getAmplitude();

  /**
   * 返回錄音文件完整路徑
   * @return
   */
  public String getFilePath();

}

個(gè)人寫的一個(gè)錄音實(shí)踐策略

package com.example.recordtest;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.media.MediaRecorder;
import android.os.Environment;

public class AudioRecorder implements RecordStrategy {

  private MediaRecorder recorder;
  private String fileName;
  private String fileFolder = Environment.getExternalStorageDirectory()
      .getPath() + "/TestRecord";

  private boolean isRecording = false;

  @Override
  public void ready() {
    // TODO Auto-generated method stub
    File file = new File(fileFolder);
    if (!file.exists()) {
      file.mkdir();
    }
    fileName = getCurrentDate();
    recorder = new MediaRecorder();
    recorder.setOutputFile(fileFolder + "/" + fileName + ".amr");
    recorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 設(shè)置MediaRecorder的音頻源為麥克風(fēng)
    recorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);// 設(shè)置MediaRecorder錄制的音頻格式
    recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);// 設(shè)置MediaRecorder錄制音頻的編碼為amr
  }

  // 以當(dāng)前時(shí)間作為文件名
  private String getCurrentDate() {
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HHmmss");
    Date curDate = new Date(System.currentTimeMillis());// 獲取當(dāng)前時(shí)間
    String str = formatter.format(curDate);
    return str;
  }

  @Override
  public void start() {
    // TODO Auto-generated method stub
    if (!isRecording) {
      try {
        recorder.prepare();
        recorder.start();
      } catch (IllegalStateException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }

      isRecording = true;
    }

  }

  @Override
  public void stop() {
    // TODO Auto-generated method stub
    if (isRecording) {
      recorder.stop();
      recorder.release();
      isRecording = false;
    }

  }

  @Override
  public void deleteOldFile() {
    // TODO Auto-generated method stub
    File file = new File(fileFolder + "/" + fileName + ".amr");
    file.deleteOnExit();
  }

  @Override
  public double getAmplitude() {
    // TODO Auto-generated method stub
    if (!isRecording) {
      return 0;
    }
    return recorder.getMaxAmplitude();
  }

  @Override
  public String getFilePath() {
    // TODO Auto-generated method stub
    return fileFolder + "/" + fileName + ".amr";
  }

}

MainActivity

package com.example.recordtest;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;

public class MainActivity extends Activity {

  RecordButton button;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    button = (RecordButton) findViewById(R.id.btn_record);
    button.setAudioRecord(new AudioRecorder());
  }


  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
  }

}

源碼下載:Android仿微信語音對(duì)講錄音

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

文章題目:Android仿微信語音對(duì)講錄音功能
路徑分享:http://aaarwkj.com/article8/jjgiip.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護(hù)電子商務(wù)、定制網(wǎng)站、虛擬主機(jī)、網(wǎng)站改版用戶體驗(yàn)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)公司
亚洲一区二区三区熟妇| 1区2区3区精品视频| 熟女少妇精品一区二区三区| 中文字幕久久一区二区三区| 国产高清不卡一区二区| 日本免费精品一区二区三区中 | 国产精品久久综合网| 九九热这里面只有精品| 亚洲欧美成人高清在线观看| 亚洲国产欧美日韩综合| 国产成年人在线免费观看| 国产精品欧美日韩中文| 国产真人作爱视频免费| 一区二区三区欧美日韩| 亚洲欧美午夜不卡视频| 日本午夜诱惑在线观看| 亚州无吗一区二区三区| 成人在线免费黄色小说| 国产日产亚洲综合一区| 欧美日韩国产一区在线观看| 欧美久久久久综合一区| 亚洲无线码一区国产欧美国日 | 国产剧情av在线播放| 麻豆国产传媒片在线看| av资源天堂第一区第二区第三区 | 国产欧美日韩另类视频| 91精品手机在线观看| 国产亚洲欧美成人精品久久| 国产三级三级三级免费看| 日本免费熟女一区二区| 在线看岛国毛片十八禁| 日本一区二区电影在线看| 欧美亚洲另类在线第一页| 青青草成年人免费视频| 国产av高清亚洲精品高清二部| 海外成人永久免费视频| 视频在线免费观看97| 国产成十人十综合十亚洲| 蜜臀一二区免费在线视频| 精品人妻va人妻中文字幕麻豆| 漂亮人妻被中出中文字幕|