1.播放应用的资源文件(res/raw/)
java代码示例:
MainActivity.java
public class MainActivity extends AppCompatActivity {
private MediaPlayer mediaPlayer;
private SeekBar seekBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
seekBar = (SeekBar) findViewById(R.id.seekBar);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
//获取拖动结束之后的位置
int progress=seekBar.getProgress();
//跳转到某个位置播放
mediaPlayer.seekTo(progress);
}
});
}
public void play(View view){
ImageButton imageButton= (ImageButton) view;
if(mediaPlayer==null){
//播放内存卡中的音频
mediaPlayer=new MediaPlayer();
//设置类型
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
/* 得到文件路径 *//* 注:文件存放在SD卡的根目录,一定要进行prepare()方法,使硬件进行准备 */
File file = new File(Environment.getExternalStorageDirectory(),"a.mp3");
try{
/* 为MediaPlayer 设置数据源 */
mediaPlayer.setDataSource(file.getAbsolutePath());
/* 准备 */
mediaPlayer.prepare();
}catch(Exception ex){
ex.printStackTrace();
}
mediaPlayer.start();
// 把图标变为暂停图标
imageButton.setImageResource(android.R.drawable.ic_media_pause);
//获取音乐的总时长
int duration=mediaPlayer.getDuration();
//设置进度条的最大值为音乐总时长
seekBar.setMax(duration);
new MyThread().start();
// //实例化MediaPlayer
/// mediaPlayer = MediaPlayer.create(this, R.raw.one);
mediaPlayer.start();
// //把图标变为暂停图标
// imageButton.setImageResource(android.R.drawable.ic_media_pause);
// //获取音乐的总时长
// int duration=mediaPlayer.getDuration();
// //设置进度条的最大值为音乐总时长
// seekBar.setMax(duration);
// new MyThread().start();
}else if(mediaPlayer.isPlaying()){
mediaPlayer.pause();
//把图标变为播放图标
imageButton.setImageResource(android.R.drawable.ic_media_play);
}else{
mediaPlayer.start();
//把图标变为暂停图标
imageButton.setImageResource(android.R.drawable.ic_media_pause);
}
}
class MyThread extends Thread{
@Override
public void run() {
super.run();
while(seekBar.getProgress()<=seekBar.getMax()){
//获取当前位置音乐播放的位置
int currentPosition=mediaPlayer.getCurrentPosition();
//让进度条滚动起来
seekBar.setProgress(currentPosition);
}
}
}
}
简单的一个布局文件:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp"
android:layout_marginLeft="16dp"
>
<SeekBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/seekBar" />
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/ic_media_play"
android:onClick="play"
/>
</LinearLayout>
程序说明:
以上例子介绍了MediaPlayer在播放应用内的音频和播放内存卡中的音频步骤和方式.
下面介绍的是另外一种方法
2.播放外部存储上的音频资源文件(sdcard)
代码实现
1)导入歌曲到手机SD卡的qqmusic/song目录中,这里我随便导入了几首歌曲:《三生三世》、《爱丫爱丫》、《安和桥》和《和你在一起》,路径可随自己而定。
新建一个类MusicService继承Service,在类中定义一个MyBinder,有一个方法用于返回MusicService本身,在重载onBind()方法的时候返回
public final IBinder binder = new MyBinder();
public class MyBinder extends Binder{
MusicService getService() {
return MusicService.this;
}
}
/**
* onBind 是 Service 的虚方法,因此我们不得不实现它。
* 返回 null,表示客服端不能建立到此服务的连接。
*/
@Override
public IBinder onBind(Intent intent) {
return binder;
}
2)在MusicService中,声明一个MediaPlayer变量,进行设置歌曲路径,这里我选择歌曲1作为初始化时候的歌曲
private String[] musicDir = new String[]{
Environment.getExternalStorageDirectory().getAbsolutePath()+"/qqmusic/song/三生三世.mp3",
Environment.getExternalStorageDirectory().getAbsolutePath()+"/qqmusic/song/爱丫爱丫.mp3",
Environment.getExternalStorageDirectory().getAbsolutePath()+"/qqmusic/song/安和桥.mp3",
Environment.getExternalStorageDirectory().getAbsolutePath() +"/qqmusic/song/和你在一起.mp3"};
private int musicIndex = 1;
public static MediaPlayer mp = new MediaPlayer();
public MusicService() {
try {
musicIndex = 1;
mp.setDataSource(musicDir[musicIndex]);
mp.prepare();
} catch (Exception e) {
e.printStackTrace();
}
}
//播放/暂停按钮
public void playOrPause() {
if(mp.isPlaying()){
mp.pause();
} else {
mp.start();
}
}
//下一首
public void nextMusic() {
if(mp != null && musicIndex < 3) {
mp.stop();
try {
mp.reset();
mp.setDataSource(musicDir[musicIndex+1]);
musicIndex++;
mp.prepare();
mp.seekTo(0);
mp.start();
} catch (Exception e) {
Log.d("hint", "can't jump next music");
e.printStackTrace();
}
}else {
Toast.makeText(this, "没有更多歌曲啦", Toast.LENGTH_SHORT).show();
}
}
//上一首
public void preMusic() {
if(mp != null && musicIndex > 0) {
mp.stop();
try {
mp.reset();
mp.setDataSource(musicDir[musicIndex-1]);
musicIndex--;
mp.prepare();
mp.seekTo(0);
mp.start();
} catch (Exception e) {
Log.d("hint", "can't jump pre music");
e.printStackTrace();
}
}else{
Toast.makeText(this, "已经是第一首啦", Toast.LENGTH_SHORT).show();
}
}
4)注册MusicService并赋予权限,允许读取外部存储空间。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<service android:name="com.pxd.mediaplayer.MusicService" android:exported="true"/>
以上步骤是MusicService类核心代码。
接下来我们需在
5)MainAcitvity中声明ServiceConnection,调用bindService保持与MusicService通信,通过intent的事件进行通信,在onCreate()函数中绑定Service
private ServiceConnection sc = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
musicService = ((MusicService.MyBinder)iBinder).getService();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
musicService = null;
}
};
private void bindServiceConnection() {
Intent intent = new Intent(Main.this, MusicService.class);
startService(intent);
bindService(intent, sc, this.BIND_AUTO_CREATE);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
musicService = new MusicService();
bindServiceConnection();
seekBar = (SeekBar)this.findViewById(R.id.MusicSeekBar);
seekBar.setProgress(musicService.mp.getCurrentPosition());
seekBar.setMax(musicService.mp.getDuration());
musicBegin = (TextView)this.findViewById(R.id.MusicBegin);
musicTime = (TextView)this.findViewById(R.id.MusicTime);
musicStatus=(TextView)this.findViewById(R.id.MusicStatus);
btnPlayOrPause = (ImageButton) findViewById(R.id.BtnPlayorPause);
Log.d("hint", Environment.getExternalStorageDirectory().getAbsolutePath()+"/You.mp3");
}
bindService函数回调onSerciceConnented函数,通过MusiceService函数下的onBind()方法获得binder对象并实现绑定
6)通过Handle实时更新UI,这里主要使用了post方法并在Runnable中调用postDelay方法实现实时更新UI,Handle.post方法在onResume()中调用,使得程序刚开始时和重新进入应用时能够更新UI
在Runnable中更新SeekBar的状态,并设置SeekBar滑动条的响应函数,使歌曲跳转到指定位置
public android.os.Handler handler = new android.os.Handler();
public Runnable runnable = new Runnable() {
@Override
public void run() {
if(musicService.mp.isPlaying()) {
musicStatus.setText("状态:正在播放.....");
btnPlayOrPause.setImageResource(android.R.drawable.ic_media_pause);
} else {
musicStatus.setText("状态:未播放");
btnPlayOrPause.setImageResource(android.R.drawable.ic_media_play);
}
musicBegin.setText(time.format(musicService.mp.getCurrentPosition()));
musicTime.setText(time.format(musicService.mp.getDuration()));
seekBar.setProgress(musicService.mp.getCurrentPosition());
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
musicService.mp.seekTo(seekBar.getProgress());
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
handler.postDelayed(runnable, 100);
}
};
@Override
protected void onResume() {
if(musicService.mp.isPlaying()) {
musicStatus.setText("状态:正在播放.....");
btnPlayOrPause.setImageResource(android.R.drawable.ic_media_pause);
} else {
musicStatus.setText("状态:未播放");
btnPlayOrPause.setImageResource(android.R.drawable.ic_media_play);
}
seekBar.setProgress(musicService.mp.getCurrentPosition());
seekBar.setMax(musicService.mp.getDuration());
handler.post(runnable);
super.onResume();
Log.d("hint", "handler post runnable");
}
7)给每个按钮设置响应函数,在onDestroy()中添加解除绑定,避免内存泄漏
public void onClick(View view) {
switch (view.getId()) {
case R.id.BtnPlayorPause:
musicService.playOrPause();
break;
case R.id.BtnQuit:
handler.removeCallbacks(runnable);
unbindService(sc);
try {
System.exit(0);
} catch (Exception e) {
e.printStackTrace();
}
break;
case R.id.btnPre:
musicService.preMusic();
// Toast.makeText(musicService, "上一首切换", Toast.LENGTH_SHORT).show();
break;
case R.id.btnNext:
musicService.nextMusic();
//Toast.makeText(musicService, "下一首切换", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
@Override
public void onDestroy() {
unbindService(sc);
super.onDestroy();
}
8)在Button中赋予onClick属性指向接口函数
main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".Main">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/MusicStatus"
android:layout_gravity="center_horizontal"
android:textSize="20sp"
android:text=""/>
<SeekBar
android:layout_marginTop="16dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/MusicSeekBar"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/MusicBegin"
android:text=" "/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/MusicTime"
android:layout_marginLeft="270dp"
android:text=" "/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center_horizontal">
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnPre"
android:src="@android:drawable/ic_media_previous"
android:onClick="onClick" />
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/BtnPlayorPause"
android:src="@android:drawable/ic_media_play"
android:onClick="onClick"
/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnNext"
android:src="@android:drawable/ic_media_next"
android:onClick="onClick"/>
</LinearLayout>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/BtnQuit"
android:text="退出"
android:onClick="onClick"/>
</LinearLayout>
这样就可以实现简单音乐播放器的播放。
效果图
总结
- 读取SD卡内存的时候,应该使用android.os.Environment库中的getExternalStorageDirectory()方法,然而并不能生效。应该再使用getAbsolutePath()获取绝对路径后读取音乐才生效。
- 切换歌曲的时候try块不能正确执行。检查过后,也是执行了stop()函数后再重新setDataSource()来切换歌曲的,但是没有效果。查阅资料后,发现setDataSource()之前需要调用reSet()方法,才可以重新设置歌曲。
简述如何使用Handler实时更新UI
方法一:
Handle的post方法,在post的Runable的run方法中,使用postDelay方法再次post该Runable对象,在Runable中更新UI,达到实时更新UI的目的
方法二:
多开一个线程,线程写一个持续循环,每次进入循环内即post一次Runable,然后休眠1000ms,亦可做到实时更新UI