您的位置:首页 > 移动开发 > Android开发

Android多媒体功能的实现上(音频,视频,相机,录音)

2016-07-02 17:08 597 查看
       在做App的时候,为了使App更加的吸引人,我们通常会加入一些多媒体功能,这也是Android中非常重要的功能之一,在刚学Android的时候,自己尝试着模仿QQ音乐做了一款音视频播放器,虽然做的不怎么样,但是大体的功能都实现了。在之后,我也不断的完善,有了一些自己的见解(源码地址都在文章末)。

        首先是Android中的音频播放功能,在这里我主要涉及到的是MediaPlayer类,这个类中的方法不全讲,涉及到细节的我知道的也不是很多,主要还是要看源代码。在此我只来讲述它的使用。

         播放音频文件,首先要找到它的位置,然后生成MediaPlayer的新对象,调用相应的方法,加载相应位置的文件,进行播放。

        文件的位置主要包括三个来源:1用户在文件中自带的Resource资源2在SD卡或其它路径下的文件3网络上的文件

1播放Resource资源下的音频文件: 

<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">if(this.player == null){
//   this.player = new MediaPlayer();

this.player = MediaPlayer.create(context, R.raw.a1);
//    this.player.prepare()   ;
}
//监听器判断是否结束
this.player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub
stop();
}
});
this.player.start();
}</span></span></span></span>

如果res文件夹下没有raw文件,直接新建一个raw文件,然后放入音频视频即可。

2播放SD卡或其他路径下的文件:

<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">       MediaPlayer   mediaplayer = new MediaPlayer();     //生成一个MediaPlayer的对象
mediaplayer.setDataSource("/sdcard/Wiz Khalifa-See you again.mp3");   //装载音频文件的位置
mediaplayer.prepare();                                                                                                         //准备
mediaplayer.start();                                                                                                            //开始播放</span></span></span>

setDataSource函数的参数为你播放文件的位置,必须在start之前prepare,简单的四句代码,即可播放音乐

3播放网络上的文件:

<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">Uri uri = Uri.parse("http://192.168.0.135:8080/jsonweb/WebRoot/musics/starj.mp3");
//     Uri uri = Uri.parse("http://192.168.0.135:8080/raw1/starj.mp3");
media = MediaPlayer.create(startmusic.this, uri);
media.setOnPreparedListener(new OnPreparedListener(){

@Override
public void onPrepared(MediaPlayer mp) {
// TODO Auto-generated method stub
media.start();
}
});</span></span></span>


我们是否感觉非常的简单,确实几句代码就搞定了,但是在实现了这个播放的效果后,我们会感觉功能很单一,怎样去暂停音乐后播放,怎样快进,快退音乐,怎样去通过进度条控制,为何关闭了当前的Activity,音乐还在播放。或者其他的一些功能,接下来我就以播放SD卡路径下的音乐作为例子来实现这些功能。(还会延伸一些知识点)

MediaPlayer常用的方法(如下图,看不懂可上网查阅)



功能1 :实现音乐的暂停播放和停止播放(需用到prepare ,start,pause,stop,reset函数,以按钮的形式实现)

<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">//播放音乐
public  void bofang() throws IllegalStateException, IOException {
if( mediaplayer == null){

mediaplayer = new MediaPlayer();     //生成一个MediaPlayer的对象
mediaplayer.setDataSource("/sdcard/Wiz Khalifa-See you again.mp3");   //装载音频文件的位置
}
if(flag == 0 || flag == 2)
mediaplayer.prepare();               //准备
mediaplayer.start();                                                                                                                //开始播放
}
//暂停音乐
public   void   zanting(){
if(mediaplayer != null && mediaplayer.isPlaying()){

mediaplayer.pause();
flag =1;
}
}
//停止音乐
public  void   tingzhi(){
if(mediaplayer != null && mediaplayer.isPlaying()){
mediaplayer.stop();
mediaplayer.reset();   //使mediapalyer处于空闲状态</span>
<span style="font-size:18px;">          mediaplayer.release();  //切记停止音乐后,一定要把它释放掉
mediaplayer = null;    //使mediaplayer为空
flag =2;
}
}</span></span></span></span>

以上三个函数可以实现播放当前暂停处的音乐,暂停哪里就播放哪里,停止播放为从头开始播放。使用了MediaPlayer提供的方法。此代码中最重要的一点是mediaplayer这个对象,也就是你new的这个对象,如果你只播放一首歌,那么就new 一次,如果你要播放很多歌那就new 很多次。也就是说new出的这个对象,代表着相对应你选择的歌曲的生命周期,所有的功能都在这个对象上完成。如果有很多歌,就多new几个。

功能 2 :使用进度条来控制音乐的快进快退,播放选到的位置,同步播放文件

此功能需要用到seekbar控件的功能,需要了解seekbar控件的方法

xml文件seekbar属性

<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">  <SeekBar
android:id="@+id/progress_bar"
android:layout_width="300dip"
android:layout_height="wrap_content"
android:maxHeight="20dip"
android:minHeight="15dip"
android:paddingLeft="18dip"
android:paddingRight="18dip"
android:max="100"/></span></span></span>

以上xml文件显示的就是一个普通的进度条控件,现在需要对它进行操作,分为4个功能:

1 为进度条设置播放音频的时间长度,也就是说一个进度条代表一首歌的长度

<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">seekbar.setMax(mediaplayer.getDuration());   //给seekbar添加具体的时间</span></span></span></span>


这句代码使用了setMax方法和getDuration方法,获得当前mediaplayer对象的一首歌的长度然后赋值给进度条

2 使进度条能够与音频文件实现同步,音频文件播放到哪,进度条就在哪

<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"> <span style="font-size:18px;">public class seekbartongbu implements Runnable{

@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
Thread.sleep(1000);
seekbar.setProgress(mediaplayer.getCurrentPosition()); //获得当前播放的进度值
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}</span></span></span></span>


这里同步的话,必须使用线程,主线程用来播放音乐,子线程用来同步音乐,用到了setProgress方法和getCurrentPosition方法,获得当前音乐文件的播放位置,然后赋值给seekbar.

3 显示当前音乐的播放时间和音乐的总时间

 
<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"> <TextView
android:id="@+id/currentiem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dip"
android:text="当前时间"/>
<TextView
android:id="@+id/wholetime"
android:layout_width="wrap_content"
android:layout_marginTop="5dip"
android:layout_height="wrap_content"
android:text="总时间"/>
用两个Textview控件来显示时间,并且把它们俩放到seekbar的两端
longshijian = mediaplayer.getDuration();
longshijian1 = new haomiaotoshijian().formattime(longshijian);
zongshijian.setText(longshijian1);</span></span></span>


获取当前mediaplayer对象的长度,因为getDuration方法得到的是音乐文件的总时长也就是毫秒数,所有需要把它转换为正常的时间显示:

<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">public class haomiaotoshijian {

public haomiaotoshijian(){

}
//把毫秒数变成时间格式 如 01:00
public  String  formattime(long time){
time = time/ 1000;
String strHour = "" + (time/3600);
String strMinute = "" + time%3600/60;
String strSecond = "" + time%3600%60;
strHour = strHour.length() < 2? "0" + strHour: strHour;
strMinute = strMinute.length() < 2? "0" + strMinute: strMinute;
strSecond = strSecond.length() < 2? "0" + strSecond: strSecond;
String strRsult = "";

if (!strHour.equals("00"))
{
strRsult += strHour + ":";
}

if (!strMinute.equals("00"))
{
strRsult += strMinute + ":";
}

strRsult += strSecond;

return strRsult;
}</span></span></span></span>


转化好后赋给TextView控件即可

4 实现拉动进度条,播放相对应的位置

实现OnSeekBarChangeListener接口,重写三个方法OnProgressChanged,OnStartTrackingTouch,OnStopTrackingTouch这三个方法

<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
String nowtime = new haomiaotoshijian().formattime(progress);
//Log.i(TAG, progress+"");
new chuandishuju().setCurrentprogress(progress);
if(nowtime.contains(":")){
bofangshijian.setText(nowtime);
}
else
bofangshijian.setText("00:"+nowtime);

}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
if(bofangmusic.player != null)
bofangmusic.player.seekTo(seekbar.getProgress());
else{
xiayishoumusic.player.seekTo(seekbar.getProgress());
}
}</span>
</span></span></span>


onProgressChanged方法:当进度条改变时,触发的操作

onStartTrackingTouch方法 :进度条刚开始拉动,触发的操作

onStopTrackingTouch
方法 :进度条停止拖动发生的操作

这四个功能实现后,就完成了一个简单的对音频文件的操作了,简单的功能就这样完成了,如果你想添加更多的复杂的功能,只需要在这个基础上自己改动即可,有什么不对的地方,还请大家指出,源代码中的音频的播放地址根据自己所放文件的地址填写。

源代码地址:http://download.csdn.net/detail/danielntz/9565737

在完成了以上的功能后,当我们关闭此程序后,发现音乐还在响,这是因为Mediaplayer本身是一个后台服务的类,它与程序也就是Activity是分开的,较好一点的理解就是程序是一个进程,Mediaplayer是一个进程,关闭程序只是把程序的进程关闭了,Mediaplayer的进程还在执行,所以音乐还在响。(我在魅族和中兴两部手机上做了测试,因为魅族系统是对Android系统的改进,所以有些情况不会发生,主要是关于底层是Android系统的)为了防止这种情况的发生,我们可以有以下两种做法

1 根据Activity的生命周期来控制Mediaplayer

<span style="font-size:18px;">@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
mediaplayer.release();
mediaplayer = null;
endtongbu = false;
startService(new Intent(action));
Log.i(TAG, "guanbi");
}</span>
2 使用Service后台服务组件,把Mediaplayer类写在Service中(推荐使用这种类型,当把程序关掉后,音乐任然在进行,在后台,有源代码),主要分为四个步骤:

2.1 Service的配置(在配置文件中)

<service android:name="com.example.testboke.MusicService">
<intent-filter>
<action android:name="com.example.testboke.MusicService"/>
</intent-filter>
</service> MusicService为继承Service的类
2.2 Service中方法的覆盖

继承Servcie后通常会产生OnBind(),OnCreate(),OnStartCommand方法,通常当startService后,就会运行OnCreate()方法,然后是OnStartCommand()方法,之后如果再运行Service的话,就只运行OnStartCommand的方法,OnCreate()方法只在先前运行一次,除非stopService后,再startService后才执行OnCreate()方法,获取从Activity传过来的值是在OnStartCommand方法中获取

2.3 Activity与Service交互

2.3.1与普通的Activity与Activity之间的交互相同,通过Intent来进行数据的传递(Activity向Service传递数据)

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.start:
Bundle bundle = new Bundle();
bundle.putSerializable("start", "start");

startService(new Intent(action).putExtras(bundle));
receivewholetime = true;

break;

case R.id.pause:
Bundle bundle1= new Bundle();
bundle1.putSerializable("start", "pause");
startService(new Intent(action).putExtras( bundle1));

break;

case R.id.stop:
Bundle bundle2 = new Bundle();
bundle2.putSerializable("start", "stop");
startService(new Intent(action).putExtras(bundle2));

break;
}
}2.3.2 Service获取Activity传过来的数据(Service获取数据通常在OnStartCommand的方法中)
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
Log.i(TAG, "sererere");
judgefunction = (String)intent.getExtras().getSerializable("start");
if(judgefunction.equals("start")){
try {
bofang();
endtongbu = true;
startthread = true;
if(startthread){
new Thread(new tongbu()).start();
startthread = false;
}

} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(judgefunction.equals("pause")){
zanting();
}
if(judgefunction.equals("stop")){
tingzhi();
}
return super.onStartCommand(intent, flags, startId);

}

2.4 Service与Activity交互(有很多种方法,在此使用BroadCast广播类方法)

2.4.1注册广播接收 //注册广播接收
receiver = new MyReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(action);
SurfaceFunction.this.registerReceiver(receiver, filter);2.4.2接收广播(Activity获取Service传递过来的数据)
public class MyReceiver extends BroadcastReceiver{

@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if(receivewholetime){
int time = intent.getExtras().getInt("wholetime");
longshijian1 = new haomiaotoshijian().formattime(time); //歌曲总时间转换格式
Log.i(TAG, "总时间" + longshijian1);
wholetime.setText(longshijian1);
seekbar.setMax(time);
receivewholetime = false;
}

int progress = (int)intent.getExtras().getInt("progress");
seekbar.setProgress(progress);
String currenttimeone = new haomiaotoshijian().formattime(progress);
currenttime.setText(currenttimeone);

}

}2.4.3发送广播(Service向Activity发送数据)
//发送广播
Intent intent=new Intent();
intent.putExtra("wholetime", longshijian);
intent.setAction("com.example.testboke.MusicService");
sendBroadcast(intent);上述就是完整的步骤,希望大家也可以给出我建议,以Service形式的mini音频播放器源代码地址:http://download.csdn.net/detail/danielntz/9575869
有一些功能我自己就没有实现了,比如进度条控制的快进快退,有兴趣的同学可以自己试着写,这个代码运行后,关闭了程序后,音乐还在播放,如果想停止,就在你需要停止的地方添加stopService方法即可

 

 

 

 

 

 

 

 

 

      

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: