热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android多媒体之MediaPlayer

Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用Med


Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的。


 


MediaPlayer的生命周期


 



 


这张状态转换图清晰的描述了 MediaPlayer 的各个状态,也列举了主要的方法的调用时序,每种方法只能在一些特定的状态下使用,如果使用时 MediaPlayer 的状态不正确则会引发 IllegalStateException 异常 。


 


Idle 状态: 当使用 new() 方法创建一个 MediaPlayer 对象或者调用了其 reset() 方法时,该 MediaPlayer 对象处于 idle 状态。这两种方法的一个重要差别就是:如果在这个状态下调用了 getDuration() 等方法(相当于调用时机不正确),通过 reset() 方法进入 idle 状态的话会触发 OnErrorListener.onError() ,并且 MediaPlayer 会进入 Error 状态;如果是新创建的 MediaPlayer 对象,则并不会触发 onError(), 也不会进入 Error 状态。


 


End 状态: 通过 release() 方法可以进入 End 状态,只要 MediaPlayer 对象不再被使用,就应当尽快将其通过 release() 方法释放掉,以释放相关的软硬件组件资源,这其中有些资源是只有一份的(相当于临界资源)。如果 MediaPlayer 对象进入了 End 状态,则不会在进入任何其他状态了。


 


Initialized 状态: 这个状态比较简单, MediaPlayer 调用 setDataSource() 方法就进入 Initialized 状态,表示此时要播放的文件已经设置好了。


 


Prepared 状态: 初始化完成之后还需要通过调用 prepare() 或 prepareAsync() 方法,这两个方法一个是同步的一个是异步的,只有进入 Prepared 状态,才表明 MediaPlayer 到目前为止都没有错误,可以进行文件播放。


 


Preparing 状态: 这个状态比较好理解,主要是和 prepareAsync() 配合,如果异步准备完成,会触发 OnPreparedListener.onPrepared() ,进而进入 Prepared 状态。


 


Started 状态: 显然, MediaPlayer 一旦准备好,就可以调用 start() 方法,这样 MediaPlayer 就处于 Started 状态,这表明 MediaPlayer 正在播放文件过程中。可以使用 isPlaying() 测试 MediaPlayer 是否处于了 Started 状态。 如果播放完毕,而又设置了循环播放,则 MediaPlayer 仍然会处于 Started 状态,类似的,如果在该状态下 MediaPlayer 调用了 seekTo() 或者 start() 方法均可以让 MediaPlayer 停留在 Started 状态。


 


Paused 状态: Started 状态下 MediaPlayer 调用 pause() 方法可以暂停 MediaPlayer ,从而进入 Paused 状态, MediaPlayer 暂停后再次调用 start() 则可以继续 MediaPlayer 的播放,转到 Started 状态,暂停状态时可以调用 seekTo() 方法,这是不会改变状态的。


 


Stop 状态: Started 或者 Paused 状态下均可调用 stop() 停止 MediaPlayer ,而处于 Stop 状态的 MediaPlayer 要想重新播放,需要通过 prepareAsync() 和 prepare() 回到先前的 Prepared 状态重新开始才可以。


 


PlaybackCompleted 状态: 文件正常播放完毕,而又没有设置循环播放的话就进入该状态,并会触发 OnCompletionListener 的 onCompletion() 方法。此时可以调用 start() 方法重新从头播放文件,也可以 stop() 停止 MediaPlayer ,或者也可以 seekTo() 来重新定位播放位置。


 


Error 状态: 如果由于某种原因 MediaPlayer 出现了错误,会触发 OnErrorListener.onError() 事件,此时 MediaPlayer 即进入 Error 状态,及时捕捉并妥善处理这些错误是很重要的,可以帮助我们及时释放相关的软硬件资源,也可以改善用户体验。通过 setOnErrorListener(android.media.MediaPlayer.OnErrorListener) 可以设置该监听器。如果 MediaPlayer 进入了 Error 状态,可以通过调用 reset() 来恢复,使得 MediaPlayer 重新返回到 Idle 状态。


 


下面的程序是使用MediaPlayer播放音频文件并通过SeekBar显示进度的程序:


 





package com.example.musicplay;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
//import android.widget.SeekBar.OnSeekBarChangeListener;

@SuppressLint(
"HandlerLeak")
public class MusicActivity extends Activity
{
private MediaPlayer mp;
private Button play;
private Button pause;
private Button speed;
private Button stop;
private SeekBar seekbar;

private Handler myHandler;

@SuppressLint(
"HandlerLeak")
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_music);

initMusic();
initPlayerControl();
initHandler();

new Timer().schedule(new TimerTask()
{
@Override
public void run()
{
if(mp == null)
return;
if(mp.isPlaying()&&!seekbar.isPressed())
{
Message msg
= new Message();
msg.what
= 0x123;
myHandler.sendMessage(msg);
}
}
},
0,1000);
}

//初始化Handle
private void initHandler()
{
myHandler
= new Handler()
{
@Override
public void handleMessage(Message msg)
{
if(msg.what == 0x123)
{
int position = mp.getCurrentPosition();
seekbar.setProgress(position );
}
}

};
}
//初始化mp,加载音乐
private void initMusic()
{
mp
= MediaPlayer.create(this, R.raw.xxx);
mp.setLooping(
false);
}
//初始化播放器控件
private void initPlayerControl()
{
play
= (Button)findViewById(R.id.play);
pause
= (Button)findViewById(R.id.pause);
speed
= (Button)findViewById(R.id.speed);
stop
= (Button)findViewById(R.id.stop);
seekbar
= (SeekBar)findViewById(R.id.my_seekbar);

play.setEnabled(
true);
pause.setEnabled(
false);
speed.setEnabled(
false);
stop.setEnabled(
false);
initSeekBar();

play.setOnClickListener(
new OnClickListener()
{
@Override
public void onClick(View v)
{
if(mp!=null)
{
mp.start();
play.setEnabled(
false);
pause.setEnabled(
true);
speed.setEnabled(
true);
stop.setEnabled(
true);
}
}
});

pause.setOnClickListener(
new OnClickListener()
{
@Override
public void onClick(View v)
{
if(mp!=null)
{
mp.pause();
play.setEnabled(
true);
pause.setEnabled(
false);
speed.setEnabled(
false);
stop.setEnabled(
false);
}
}
});

speed.setOnClickListener(
new OnClickListener()
{
@Override
public void onClick(View v)
{
if(mp!=null)
{
int positon = mp.getCurrentPosition();
int duration = mp.getDuration();
int headon = duration/20;
mp.seekTo(positon
+headon);
}
}
});

stop.setOnClickListener(
new OnClickListener()
{
@Override
public void onClick(View v)
{
if(mp!=null)
{
mp.stop();
play.setEnabled(
true);
pause.setEnabled(
false);
speed.setEnabled(
false);
stop.setEnabled(
false);
try
{
mp.prepare();
mp.seekTo(
0);
seekbar.setProgress(
0);
}
catch (IllegalStateException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
});
}

//初始化seekbar拖拽效果
private void initSeekBar()
{
seekbar.setMax(mp.getDuration());
// seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener()
// {
// @Override
// public void onStopTrackingTouch(SeekBar seekBar)
// {
// }
//
// @Override
// public void onStartTrackingTouch(SeekBar seekBar)
// {
// }
//
// @Override
// public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
// {
// if(mp == null)
// return;
// mp.seekTo(progress);
// }
// });
}
@Override
protected void onDestroy()
{
if(mp !=null)
mp.release();
}


}



<LinearLayout xmlns:android&#61;"http://schemas.android.com/apk/res/android"
xmlns:tools
&#61;"http://schemas.android.com/tools"
android:layout_width
&#61;"match_parent"
android:layout_height
&#61;"match_parent"
android:orientation
&#61;"vertical" >
<LinearLayout
android:layout_width&#61;"match_parent"
android:layout_height
&#61;"wrap_content"
android:orientation
&#61;"horizontal" >
<Button
android:id&#61;"&#64;&#43;id/play"
android:layout_width
&#61;"wrap_content"
android:layout_height
&#61;"wrap_content"
android:layout_weight
&#61;"1"
android:text
&#61;"play" />
<Button
android:id&#61;"&#64;&#43;id/pause"
android:layout_width
&#61;"wrap_content"
android:layout_height
&#61;"wrap_content"
android:layout_weight
&#61;"1"
android:text
&#61;"pause" />
<Button
android:id&#61;"&#64;&#43;id/speed"
android:layout_width
&#61;"wrap_content"
android:layout_height
&#61;"wrap_content"
android:layout_weight
&#61;"1"
android:text
&#61;"speed" />
<Button
android:id&#61;"&#64;&#43;id/stop"
android:layout_width
&#61;"wrap_content"
android:layout_height
&#61;"wrap_content"
android:layout_weight
&#61;"1"
android:text
&#61;"stop" />
LinearLayout>

<SeekBar
android:id&#61;"&#64;&#43;id/my_seekbar"
android:paddingLeft
&#61;"10dp"
android:paddingRight
&#61;"10dp"
android:layout_width
&#61;"match_parent"
android:layout_height
&#61;"wrap_content"
android:max
&#61;"100">
SeekBar>
LinearLayout>


 



 


 


转载于:https://www.cnblogs.com/Bigmouse123/p/3345706.html



推荐阅读
  • 使用TabActivity实现Android顶部选项卡功能
    本文介绍如何通过继承TabActivity来创建Android应用中的顶部选项卡。通过简单的步骤,您可以轻松地添加多个选项卡,并实现基本的界面切换功能。 ... [详细]
  • Android 开发技巧:使用 AsyncTask 实现后台任务与 UI 交互
    本文详细介绍了如何在 Android 应用中利用 AsyncTask 来执行后台任务,并及时将任务进展反馈给用户界面,提高用户体验。 ... [详细]
  • Android 中的布局方式之线性布局
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • Hadoop MapReduce 实战案例:手机流量使用统计分析
    本文通过一个具体的Hadoop MapReduce案例,详细介绍了如何利用MapReduce框架来统计和分析手机用户的流量使用情况,包括上行和下行流量的计算以及总流量的汇总。 ... [详细]
  • 本文探讨了如何利用 Android 的 Movie 类来展示 GIF 动画,并详细介绍了调整 GIF 尺寸以适应不同布局的方法。同时,提供了相关的代码示例和注意事项。 ... [详细]
  • 本文基于Java官方文档进行了适当修改,旨在介绍如何实现一个能够同时处理多个客户端请求的服务端程序。在前文中,我们探讨了单客户端访问的服务端实现,而本篇将深入讲解多客户端环境下的服务端设计与实现。 ... [详细]
  • 如何使用Maven将依赖插件一并打包进JAR文件
    本文详细介绍了在使用Maven构建项目时,如何将所需的依赖插件一同打包进最终的JAR文件中,以避免手动部署依赖库的麻烦。 ... [详细]
  • 本文详细探讨了 Android Service 组件中 onStartCommand 方法的四种不同返回值及其应用场景。Service 可以在后台执行长时间的操作,无需提供用户界面,支持通过启动和绑定两种方式创建。 ... [详细]
  • 使用Java计算两个日期之间的月份数
    本文详细介绍了利用Java编程语言计算两个指定日期之间月份数的方法。文章通过实例代码讲解了如何使用Joda-Time库来简化日期处理过程,旨在为开发者提供一个高效且易于理解的解决方案。 ... [详细]
  • iOS如何实现手势
    这篇文章主要为大家展示了“iOS如何实现手势”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“iOS ... [详细]
  • 本文探讨了在已知最终数组尺寸不会超过5000x10的情况下,如何利用预分配和调整大小的方法来优化Numpy数组的创建过程,以提高性能并减少内存消耗。 ... [详细]
  • 本文介绍了如何使用 Python 的 Pyglet 库加载并显示图像。Pyglet 是一个用于开发图形用户界面应用的强大工具,特别适用于游戏和多媒体项目。 ... [详细]
  • 本文分享了作者在使用LaTeX过程中的几点心得,涵盖了从文档编辑、代码高亮、图形绘制到3D模型展示等多个方面的内容。适合希望深入了解LaTeX高级功能的用户。 ... [详细]
  • Maven + Spring + MyBatis + MySQL 环境搭建与实例解析
    本文详细介绍如何使用MySQL数据库进行环境搭建,包括创建数据库表并插入示例数据。随后,逐步指导如何配置Maven项目,整合Spring框架与MyBatis,实现高效的数据访问。 ... [详细]
  • 函子(Functor)是函数式编程中的一个重要概念,它不仅是一个特殊的容器,还提供了一种优雅的方式来处理值和函数。本文将详细介绍函子的基本概念及其在函数式编程中的应用,包括如何通过函子控制副作用、处理异常以及进行异步操作。 ... [详细]
author-avatar
个性2402852463
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有