編輯:關於Android編程
有段時間沒有看視頻了,昨天晚上抽了點空時間,又看了下鴻洋大神的視頻教程,又抽時間寫了個學習記錄。代碼和老師講的基本一樣,網上也有很多相同的博客。我只是在AndroidStudio環境下寫的。
—-主界面代碼——
public class MainActivity extends Activity { private ListView mListView; private ArrayAdapter<Recorder> mAdapter; private List<Recorder> mDatas = new ArrayList<Recorder>(); private AudioRecorderButton mAudioRecorderButton; private View animView; @Override protected void 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.setFinishRecorderCallBack(new AudioRecorderButton.AudioFinishRecorderCallBack() { public void onFinish(float seconds, String filePath) { Recorder recorder = new Recorder(seconds, filePath); mDatas.add(recorder); //更新數據 mAdapter.notifyDataSetChanged(); //設置位置 mListView.setSelection(mDatas.size() - 1); } }); mAdapter = new RecoderAdapter(this, mDatas); mListView.setAdapter(mAdapter); //listView的item點擊事件 mListView.setOnItemClickListener(new OnItemClickListener() { public void 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(); // 播放錄音 MediaPlayerManager.playSound(mDatas.get(position).filePath, new MediaPlayer.OnCompletionListener() { public void onCompletion(MediaPlayer mp) { //播放完成後修改圖片 animView.setBackgroundResource(R.drawable.adj); } }); } }); } @Override protected void onPause() { super.onPause(); MediaPlayerManager.pause(); } @Override protected void onResume() { super.onResume(); MediaPlayerManager.resume(); } @Override protected void onDestroy() { super.onDestroy(); MediaPlayerManager.release(); }
—自定義Button——-
/** * @param * @author ldm * @description 自定義Button * @time 2016/6/25 9:26 */ public class AudioRecorderButton extends Button { // 按鈕正常狀態(默認狀態) private static final int STATE_NORMAL = 1; //正在錄音狀態 private static final int STATE_RECORDING = 2; //錄音取消狀態 private static final int STATE_CANCEL = 3; //記錄當前狀態 private int mCurrentState = STATE_NORMAL; //是否開始錄音標志 private boolean isRecording = false; //判斷在Button上滑動距離,以判斷 是否取消 private static final int DISTANCE_Y_CANCEL = 50; //對話框管理工具類 private DialogManager mDialogManager; //錄音管理工具類 private AudioManager mAudioManager; //記錄錄音時間 private float mTime; // 是否觸發longClick private boolean mReady; //錄音准備 private static final int MSG_AUDIO_PREPARED = 0x110; //音量發生改變 private static final int MSG_VOICE_CHANGED = 0x111; //取消提示對話框 private static final int MSG_DIALOG_DIMISS = 0x112; /** * @description 獲取音量大小的線程 * @author ldm * @time 2016/6/25 9:30 * @param */ private Runnable mGetVoiceLevelRunnable = new Runnable() { public void run() { while (isRecording) {//判斷正在錄音 try { Thread.sleep(100); mTime += 0.1f;//錄音時間計算 mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);//每0.1秒發送消息 } catch (InterruptedException e) { e.printStackTrace(); } } } }; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { 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; } super.handleMessage(msg); } }; public AudioRecorderButton(Context context, AttributeSet attrs) { super(context, attrs); mDialogManager = new DialogManager(context); //錄音文件存放地址 String dir = Environment.getExternalStorageDirectory() + "/ldm_voice"; mAudioManager = AudioManager.getInstance(dir); mAudioManager.setOnAudioStateListener(new AudioManager.AudioStateListener() { public void wellPrepared() { mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED); } }); // 由於這個類是button所以在構造方法中添加監聽事件 setOnLongClickListener(new OnLongClickListener() { public boolean onLongClick(View v) { mReady = true; mAudioManager.prepareAudio(); return false; } }); } public AudioRecorderButton(Context context) { this(context, null); } /** * @description 錄音完成後的回調 * @author ldm * @time 2016/6/25 11:18 * @param */ public interface AudioFinishRecorderCallBack { void onFinish(float seconds, String filePath); } private AudioFinishRecorderCallBack finishRecorderCallBack; public void setFinishRecorderCallBack(AudioFinishRecorderCallBack listener) { finishRecorderCallBack = listener; } /** * @param * @description 處理Button的OnTouchEvent事件 * @author ldm * @time 2016/6/25 9:35 */ @Override public boolean onTouchEvent(MotionEvent event) { //獲取TouchEvent狀態 int action = event.getAction(); // 獲得x軸坐標 int x = (int) event.getX(); // 獲得y軸坐標 int y = (int) event.getY(); switch (action) { case MotionEvent.ACTION_DOWN://手指按下 changeState(STATE_RECORDING); break; case MotionEvent.ACTION_MOVE://手指移動 if (isRecording) { //根據x,y的坐標判斷是否需要取消 if (wantToCancle(x, y)) { changeState(STATE_CANCEL); } else { changeState(STATE_RECORDING); } } break; case MotionEvent.ACTION_UP://手指放開 if (!mReady) { reset(); return super.onTouchEvent(event); } if (!isRecording || mTime < 0.6f) {//如果時間少於0.6s,則提示錄音過短 mDialogManager.tooShort(); mAudioManager.cancel(); // 延遲顯示對話框 mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS, 1000); } else if (mCurrentState == STATE_RECORDING) { //如果狀態為正在錄音,則結束錄制 mDialogManager.dimissDialog(); mAudioManager.release(); if (finishRecorderCallBack != null) { finishRecorderCallBack.onFinish(mTime, mAudioManager.getCurrentFilePath()); } } else if (mCurrentState == STATE_CANCEL) { // 想要取消 mDialogManager.dimissDialog(); mAudioManager.cancel(); } reset(); break; } return super.onTouchEvent(event); } /** * 恢復狀態及標志位 */ private void reset() { isRecording = false; mTime = 0; mReady = false; changeState(STATE_NORMAL); } private boolean wantToCancle(int x, int y) { // 超過按鈕的寬度 if (x < 0 || x > getWidth()) { return true; } // 超過按鈕的高度 if (y < -DISTANCE_Y_CANCEL || y > getHeight() + DISTANCE_Y_CANCEL) { return true; } return false; } /** * @param * @description 根據狀態改變Button顯示 * @author ldm * @time 2016/6/25 9:36 */ private void 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_CANCEL: setBackgroundResource(R.drawable.btn_recorder_recording); mDialogManager.wantToCancel(); setText(R.string.str_recorder_want_cancel); break; } } } }
—-對話框管理工具類——
/** * @description 對話框管理工具類 * @author ldm * @time 2016/6/25 11:53 * @param */ public class DialogManager { //彈出對話框 private Dialog mDialog; //錄音圖標 private ImageView mIcon; //音量顯示 圖標 private ImageView mVoice; //對話框上提示文字 private TextView mLable; //上下文對象 private Context mContext; public DialogManager(Context context) { this.mContext = context; } /** * @param * @description 顯示對話框 * @author ldm * @time 2016/6/25 9:56 */ public void showRecordingDialog() { //根據指定sytle實例化Dialog mDialog = new Dialog(mContext, R.style.AudioDialog); LayoutInflater inflater = LayoutInflater.from(mContext); View view = inflater.inflate(R.layout.dialog_recorder, null); mDialog.setContentView(view); 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); mDialog.show(); } /** * @param * @description 正在錄音狀態的對話框 * @author ldm * @time 2016/6/25 10:08 */ 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("手指上滑,取消發送"); } } /** * @param * @description 取消錄音狀態對話框 * @author ldm * @time 2016/6/25 10:08 */ public void wantToCancel() { if (mDialog != null && mDialog.isShowing()) { mIcon.setVisibility(View.VISIBLE); mVoice.setVisibility(View.GONE); mLable.setVisibility(View.VISIBLE); mIcon.setImageResource(R.drawable.cancel); mLable.setText("松開手指,取消發送"); } } /** * @param * @description時間過短提示的對話框 * @author ldm * @time 2016/6/25 10:09 */ public void tooShort() { 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("錄音時間過短"); } } /** * @param * @description * @author ldm * @time 2016/6/25 取消(關閉)對話框 */ public void dimissDialog() { if (mDialog != null && mDialog.isShowing()) { //顯示狀態 mDialog.dismiss(); mDialog = null; } } // 顯示更新音量級別的對話框 public void updateVoiceLevel(int level) { if (mDialog != null && mDialog.isShowing()) { //顯示狀態 mIcon.setVisibility(View.VISIBLE); mVoice.setVisibility(View.VISIBLE); mLable.setVisibility(View.VISIBLE); //設置圖片的id,我們放在drawable中的聲音圖片是以v+數字格式的 int resId = mContext.getResources().getIdentifier("v" + level, "drawable", mContext.getPackageName()); mVoice.setImageResource(resId); } } }
—-聲音播放工具類——
/** * @param * @author ldm * @description 播放聲音工具類 * @time 2016/6/25 11:29 */ public class MediaPlayerManager { //播放音頻API類:MediaPlayer private static MediaPlayer mMediaPlayer; //是否暫停 private static boolean isPause; /** * @param * filePath:文件路徑 * onCompletionListener:播放完成監聽 * @description 播放聲音 * @author ldm * @time 2016/6/25 11:30 */ public static void playSound(String filePath, MediaPlayer.OnCompletionListener onCompletionListener) { if (mMediaPlayer == null) { mMediaPlayer = new MediaPlayer(); //設置一個error監聽器 mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { public boolean onError(MediaPlayer arg0, int arg1, int arg2) { mMediaPlayer.reset(); return false; } }); } else { mMediaPlayer.reset(); } try { mMediaPlayer.setAudioStreamType(android.media.AudioManager.STREAM_MUSIC); mMediaPlayer.setOnCompletionListener(onCompletionListener); mMediaPlayer.setDataSource(filePath); mMediaPlayer.prepare(); mMediaPlayer.start(); } catch (Exception e) { } } /** * @param * @description 暫停播放 * @author ldm * @time 2016/6/25 11:31 */ public static void pause() { if (mMediaPlayer != null && mMediaPlayer.isPlaying()) { //正在播放的時候 mMediaPlayer.pause(); isPause = true; } } /** * @param * @description 重新播放 * @author ldm * @time 2016/6/25 11:31 */ public static void resume() { if (mMediaPlayer != null && isPause) { mMediaPlayer.start(); isPause = false; } } /** * @param * @description 釋放操作 * @author ldm * @time 2016/6/25 11:32 */ public static void release() { if (mMediaPlayer != null) { mMediaPlayer.release(); mMediaPlayer = null; } }
—–錄音操作工具類—–
/** * @param * @author ldm * @description 錄音管理工具類 * @time 2016/6/25 9:39 */ public class AudioManager { //AudioRecord: 主要是實現邊錄邊播(AudioRecord+AudioTrack)以及對音頻的實時處理。 // 優點:可以語音實時處理,可以實現各種音頻的封裝 private MediaRecorder mMediaRecorder; //錄音文件 private String mDir; //當前錄音文件目錄 private String mCurrentFilePath; //單例模式 private static AudioManager mInstance; //是否准備好 private boolean isPrepare; //私有構造方法 private AudioManager(String dir) { mDir = dir; } //對外公布獲取實例的方法 public static AudioManager getInstance(String dir) { if (mInstance == null) { synchronized (AudioManager.class) { if (mInstance == null) { mInstance = new AudioManager(dir); } } } return mInstance; } /** * @param * @author ldm * @description 錄音准備工作完成回調接口 * @time 2016/6/25 11:14 */ public interface AudioStateListener { void wellPrepared(); } public AudioStateListener mAudioStateListener; /** * @param * @description 供外部類調用的設置回調方法 * @author ldm * @time 2016/6/25 11:14 */ public void setOnAudioStateListener(AudioStateListener listener) { mAudioStateListener = listener; } /** * @param * @description 錄音准備工作 * @author ldm * @time 2016/6/25 11:15 */ public void prepareAudio() { try { isPrepare = 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()); // 設置MediaRecorder的音頻源為麥克風 mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 設置音頻格式為RAW_AMR mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR); // 設置音頻編碼為AMR_NB mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 准備錄音 mMediaRecorder.prepare(); // 開始,必需在prepare()後調用 mMediaRecorder.start(); // 准備完成 isPrepare = true; if (mAudioStateListener != null) { mAudioStateListener.wellPrepared(); } } catch (Exception e) { e.printStackTrace(); } } /** * @param * @description 隨機生成錄音文件名稱 * @author ldm * @time 2016/6/25 、 */ private String generateFileName() { //隨機生成不同的UUID return UUID.randomUUID().toString() + ".amr"; } /** * @param * @description 獲取音量值 * @author ldm * @time 2016/6/25 9:49 */ public int getVoiceLevel(int maxlevel) { if (isPrepare) { try { // getMaxAmplitude返回的數值最大是32767 return maxlevel * mMediaRecorder.getMaxAmplitude() / 32768 + 1;//返回結果1-7之間 } catch (Exception e) { e.printStackTrace(); } } return 1; } /** * @param * @description 釋放資源 * @author ldm * @time 2016/6/25 9:50 */ public void release() { mMediaRecorder.stop(); mMediaRecorder.reset(); mMediaRecorder = null; } /** * @param * @description 錄音取消 * @author ldm * @time 2016/6/25 9:51 */ public void cancel() { release(); if (mCurrentFilePath != null) { //取消錄音後刪除對應文件 File file = new File(mCurrentFilePath); file.delete(); mCurrentFilePath = null; } } /** * @param * @description 獲取當前文件路徑 * @author ldm * @time 2016/6/25 9:51 */ public String getCurrentFilePath() { return mCurrentFilePath; } }
代碼中有注釋,就不貼圖了,和微信語音聊天界面一樣的,所以叫仿微信嘛,呵呵。運行了也可以看到效果。所有代碼可以從這裡下載:http://xiazai.jb51.net/201611/yuanma/AndroidWXchat(jb51.net).rar
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。
Notification的使用大體步驟:1、 獲取狀態通知欄管理2、 實例化通知欄構造器3、 設置NotificationCompat.Builder4、 設置Pendi
微信開放平台和公眾平台的區別1.公眾平台面向的時普通的用戶,比如自媒體和媒體,企業官方微信公眾賬號運營人員使用,當然你所在的團隊或者公司有實力去開發一些內容,也可以調用公
最近需要用到微信的標簽功能(如下圖所示)。該功能可以添加已有標簽,也可以自定義標簽。也可以刪除已編輯菜單。研究了一番。發現還是挺有意思的,模擬實現相關功能。該
1.服務Service簡介服務(service)是Android中實現程序後台運行的程序,非常適合去執行那些不需要和用戶交互還要長期運行的任務,其運行不依賴任何用戶界面。