前言
这几天学习了慕课网上的高仿微信语音聊天功能的课程,自己动手实现了一下。在这里将其实现的过程以及代码分享下来。由于我是android的初学者,里面有不成熟的地方欢迎大家指正。
项目简单介绍
首先我们来看最终实现的几张效果图吧。如下:
我们长安按钮,就会开始录音,并且会同时弹出一个麦克风的对话框提示正在录音。如果在录音的过程中手指上滑,则会将录音取消。而如果是录音时间太短,则会提示录音时间太短,完成录音。正常录音后,会显示在按钮上方。
从图中,不难看出在整个界面里,上方是是一个 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"
好了,我们的第一步总算迈出去了。主要就是一个自定义按钮的实现。下一文章中我们将会实现对话框。