前言
這幾天學習了慕課網上的高仿微信語音聊天功能的課程,自己動手實現了一下。在這里將其實現的過程以及代碼分享下來。由於我是android的初學者,里面有不成熟的地方歡迎大家指正。
項目中所用的圖片可以在這個地方下載:
http://pan.baidu.com/disk/home
項目簡單介紹
首先我們來看最終實現的幾張效果圖吧。如下:
我們長安按鈕,就會開始錄音,並且會同時彈出一個麥克風的對話框提示正在錄音。如果在錄音的過程中手指上滑,則會將錄音取消。而如果是錄音時間太短,則會提示錄音時間太短,完成錄音。正常錄音后,會顯示在按鈕上方。
從圖中,不難看出在整個界面里,上方是是一個 ListView用來顯示錄音,而下面就是一個按鈕。按鈕有三種狀態,即正常錄音,取消錄音和無操作時的默認狀態。與按鈕對應,對話框也有三種狀態,即正常錄音,錄音取消,錄音時間太短這三種狀態。因此完成這個小項目,需要我們具備ListView的基本知識,熟悉自定義按鈕和自定義對話框。同時對android提供的錄音器類和音頻播放的類也要基本了解。
我打算將這個小項目分成以下幾步來做:
(1)完成按鈕的交互設計。
(2)完成對話框的設計。
(3)完成錄音時的代碼部分,並集成到之前的代碼里。
(4)完成播放設置,項目也從此結束。
上面只是我為了學習知識,簡單實現的UI。並不是很好看,讀者有要求,可以自己耐心調一調,讓界面好看一點。在這里我不打算講解所涉及的android中的基礎知識,而是直接寫項目的代碼。好了,我們開始第一步,完成按鈕的設計,即實現按鈕的三種狀態。
按鈕的實現
首先把主界面給搭建起來,即上方是LisetView,下面是一個我們自定義的按鈕。新建activity_main.xml,代碼如下:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical"> 6 7 <ListView 8 android:id="@+id/rec_listview" 9 android:layout_width="match_parent" 10 android:layout_height="0dp" 11 android:layout_weight="1"/> 12 13 <FrameLayout 14 android:layout_width="match_parent" 15 android:layout_height="wrap_content" 16 > 17 18 <com.fuly.util.RecoderButton 19 android:id="@+id/btn_recoder" 20 android:layout_width="120dp" 21 android:layout_height="wrap_content" 22 android:background="@drawable/btn_bg" 23 android:layout_marginTop="5dp" 24 android:layout_marginBottom="3dp" 25 android:text="@string/btn_normal" 26 android:layout_gravity="center"/> 27 <View 28 android:layout_width="match_parent" 29 android:layout_height="2dp" 30 android:background="@color/black"/> 31 32 </FrameLayout> 33 34 35 36 37 </LinearLayout>
接下來,在res文件下新建文件夾drawable,然后在里面新建btn_bg.xml。這是為自定義的按鈕提供不同狀態下的按鈕背景圖片。代碼如下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <selector 3 xmlns:android="http://schemas.android.com/apk/res/android"> 4 <!-- 沒有觸摸時的圖片 --> 5 <item 6 android:state_focused="true" 7 android:state_enabled="true" 8 android:state_pressed="false" 9 android:drawable="@drawable/btn_normal"/> 10 <!-- 觸摸時的圖片 --> 11 <item 12 android:state_enabled="true" 13 android:state_pressed="true" 14 android:drawable="@drawable/btn_press"/> 15 <item 16 android:state_enabled="true" 17 android:state_checked="true" 18 android:drawable="@drawable/btn_press"/> 19 20 21 <!-- 默認時的背景圖片--> 22 <item 23 24 android:drawable="@drawable/btn_normal"/> 25 26 </selector>
然后在res下的values文件夾下的strings里面,定義按鈕需要顯示的文本,如下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <resources> 3 4 <string name="app_name">irecoder</string> 5 <string name="action_settings">Settings</string> 6 <string name="btn_normal">按住 錄音</string> 7 <string name="btn_recoding">松開 結束</string> 8 <string name="btn_cancel">手指上滑,取消錄音 錄音</string> 9 10 11 </resources>
接來准備工作都做的差不多了。然后實現我們的自定義按鈕。新建類RecoderButton,該類繼承自Button。具體代碼如下:
1 package com.fuly.util; 2 3 4 import com.fuly.irecoder.R; 5 6 import android.content.Context; 7 import android.util.AttributeSet; 8 import android.view.MotionEvent; 9 import android.widget.Button; 10 11 12 public class RecoderButton extends Button{ 13 14 //按鈕的三個狀態 15 16 private static final int STATE_NORMAL = 1;//正常 17 private static final int STATE_RECODING = 2;//錄音狀態 18 private static final int STATE_CACLE = 3;//取消狀態 19 20 private int mCurState = STATE_NORMAL;//記錄當前按鈕狀態 21 22 private int Y = 50;//限定手指移動的上下寬度 23 24 25 26 27 public RecoderButton(Context context, AttributeSet attrs) { 28 super(context, attrs); 29 30 } 31 32 33 34 //捕捉按鈕點擊事件 35 public boolean onTouchEvent(MotionEvent event) { 36 37 int x = (int) event.getX(); 38 int y =(int)event.getY(); 39 40 switch(event.getAction()){ 41 42 43 case MotionEvent.ACTION_DOWN: 44 45 changeState(STATE_RECODING);//按下按鈕,改變按鈕狀態 46 47 break; 48 case MotionEvent.ACTION_MOVE: 49 50 if(wantCancel(x,y)){ //如果檢測到取消,則改變按鈕狀態為取消 51 52 changeState(STATE_CACLE); 53 54 }else{ 55 changeState(STATE_RECODING); 56 } 57 58 break; 59 case MotionEvent.ACTION_UP: 60 61 reset();//各種設置復位 62 63 break; 64 default: 65 break; 66 } 67 68 return super.onTouchEvent(event); 69 } 70 71 72 73 //復位 74 private void reset() { 75 76 mCurState = STATE_NORMAL; 77 changeState(STATE_NORMAL); 78 79 } 80 81 82 83 //檢查手指移動范圍,從而確定用戶是否想取消錄音 84 private boolean wantCancel(int x, int y) { 85 86 if(x<0||x>getWidth()){ 87 88 return true; 89 } 90 91 if(y<0||y>getHeight()+Y){ 92 return true; 93 } 94 return false; 95 } 96 97 98 99 //改變狀態,包括按鈕等操作 100 private void changeState(int state) { 101 102 if(mCurState != state){ 103 104 mCurState = state; 105 106 } 107 108 switch(mCurState){ 109 110 case STATE_NORMAL: 111 112 setText(R.string.btn_normal); 113 114 break; 115 case STATE_RECODING: 116 117 setText(R.string.btn_recoding); 118 119 break; 120 case STATE_CACLE: 121 122 setText(R.string.btn_cancel); 123 124 break; 125 default: 126 break; 127 128 } 129 130 } 131 132 133 134 }
最后,我們在主類中加載activity_main布局。如下:
1 package com.fuly.irecoder; 2 3 import android.os.Bundle; 4 import android.app.Activity; 5 import android.view.Menu; 6 7 public class MainActivity extends Activity { 8 9 10 protected void onCreate(Bundle savedInstanceState) { 11 super.onCreate(savedInstanceState); 12 setContentView(R.layout.activity_main); 13 } 14 15 16 @Override 17 public boolean onCreateOptionsMenu(Menu menu) { 18 // Inflate the menu; this adds items to the action bar if it is present. 19 getMenuInflater().inflate(R.menu.main, menu); 20 return true; 21 } 22 23 }
然后我們運行這個android項目即可。就會發現隨着我們手指的移動,按鈕呈現出我們想要的狀態。
當然了,為了好看,我們在AndroidManifest文件里讓app全屏顯示。只需要加上這么一句即可:
1 android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
好了,我們的第一步總算邁出去了。主要就是一個自定義按鈕的實現。下一文章中我們將會實現對話框。