实践是知识的母亲,知识是生活的明灯。这篇文章主要讲述Android开发---MediaPlayer简单音乐播放器相关的知识,希望能为你提供帮助。
android开发—MediaPlayer简单音乐播放器
功能介绍
- 实现一个简单的播放器,
类似网易云音乐形式,
功能包括:
- 播放、暂停, 停止, 退出功能;
- 后台播放功能;
- 进度条显示播放进度、拖动进度条改变进度功能;
- 播放时图片旋转, 显示当前播放时间功能;
- 界面样式
文章图片
- MediaPlayer常用方法介绍
文章图片
MediaPlayer的实现包括初始化MediaPlayer, MediaPlayer的功能实现, 包括播放、暂停、停止、离开等, 具体细节如下:
MediaPlayer的初始化包括读取一个MP3格式的音乐文件并设置其为循环播放:
public void initMediaPlayer() {
try {
//String file_path =
"
/storage/0123-4567/K.Will-Melt.mp3"
;
String file_path =
Environment.getExternalStorageDirectory().getAbsolutePath()+
"
/K.Will-Melt.mp3"
;
//String file_path =
"
/data/K.Will-Melt.mp3"
;
mediaPlayer.setDataSource(file_path);
mediaPlayer.prepare();
mediaPlayer.setLooping(true);
// 设置循环播放
} catch (Exception e) {
e.printStackTrace();
}
}
播放/暂停功能的实现放在Play/Pause按钮中, 所以需要二选一判断MediaPlayer的状态来设置音乐的播放与暂停:
public void playOrPause() {
flag+
+
;
if (flag >
=
1000) flag =
2;
which =
"
pause"
;
if(mediaPlayer.isPlaying()){
mediaPlayer.pause();
animator.pause();
} else {
mediaPlayer.start();
类似的, 停止功能的实现放在Stop按钮中, 用于停止音乐并将其置于初始态即“00:00”:
public void stop() {
which =
"
stop"
;
animator.pause();
if(mediaPlayer !=
null) {
mediaPlayer.pause();
mediaPlayer.stop();
try {
mediaPlayer.prepare();
mediaPlayer.seekTo(0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
说明: 由于调用stop()后, 有时音乐并没有马上停止, 所以我额外加了一个pause()来确保音乐停止播放。
同样, 离开功能实现放在了Quit按钮中, 主要实现整个应用的退出, 具体应包括解绑Service, 结束Activity等等:
private void quit() {
musicService.animator.end();
handler.removeCallbacks(runnable);
unbindService(sc);
try {
finish();
System.exit(0);
} catch (Exception e) {
e.printStackTrace();
}
}
2. Service的应用
- Service即“服务” , 它与Activity属于同一等级的应用程序组件, 都代表可执行的程序。不同的是Activity拥有前台运行的用户界面, 而Service不能自己运行, 需要通过某个Activity或者其他Context对象来调用。Service在后台运行, 它不能与用户直接进行交互。在默认情况下, Service运行在应用程序进程的主线程之中。可以通过Context.startService()和Context.bindService()两种方式来启动Service。
- 通过Service可以使程序在退出之后仍然能够对事件或用户操作做出反应, 或者在后台继续运行某些程序功能。
- Android赋予Services比处于不活动( inactivity) 的Activities更高的优先级, 所以它们的进程不会轻易被系统杀掉。
- Activity与Service的交互示意图为:
文章图片
有了以上知识的积累, 基本可以实现Service的必要部分,如下:
1.新建一个继承于Service的MusicService类
public class MusicService extends Service {
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.并在Activity中绑定MusicService:
@
Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getOverflowMenu();
setContentView(R.layout.activity_main);
bindServiceConnection();
musicService =
new MusicService();
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(this, MusicService.class);
startService(intent);
bindService(intent, sc, this.BIND_AUTO_CREATE);
}
3. Handler更新UI界面
通过Handler来管理UI, 最要需要用到post和postDelayed两个函数:
1. post(Runnable r)
将r加入到消息队列中( 其实就是利用UI主线程执行一段代码, 这样可以随时在其他线程中调整控件)
2. postDelayed(Runnabled r,long delayMillis)
将r加入到消息队列中, 并在指定时间之后触发( 单位为毫秒)
所以, 为了实现音乐的播放显示等功能, 需要重写Runnable的run函数, 具体实现如下:
public Handler handler =
new Handler();
public Runnable runnable =
new Runnable() {
@
Override
public void run() {isPlay.setOnClickListener(new myOnClickListener());
stop.setOnClickListener(new myOnClickListener());
quit.setOnClickListener(new myOnClickListener());
if(musicService.mediaPlayer.isPlaying()) {
stateText.setText("
Playing"
);
} else {
if (musicService.which.equals("
stop"
)){
stateText.setText("
Stop"
);
} else if (musicService.which.equals("
pause"
)){
stateText.setText("
Pause"
);
}
}
playingTime.setText(time.format(musicService.mediaPlayer.getCurrentPosition()));
totalTime.setText(time.format(musicService.mediaPlayer.getDuration()));
seekBar.setProgress(musicService.mediaPlayer.getCurrentPosition());
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@
Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
musicService.mediaPlayer.seekTo(seekBar.getProgress());
}
}@
Override
public void onStartTrackingTouch(SeekBar seekBar) {}@
Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
handler.postDelayed(runnable, 100);
}
};
4. 实现图片的旋转
图片的动画效果实现需要用到ObjectAnimator类, ObjectAnimator类可用于图片的平移、旋转、缩放等动画, 此处我们只需要用到其旋转功能, 具体实现如下:
publicvoid AnimatorAction() {
if (mediaPlayer.isPlaying()) {
animator.setDuration(5000);
animator.setInterpolator(new LinearInterpolator());
// 均速旋转
animator.setRepeatCount(ValueAnimator.INFINITE);
// 无限循环
animator.setRepeatMode(ValueAnimator.INFINITE);
animator.start();
}
}
说明: animator是ObjectAnimator的一个实例对象, setDuration为5000表示旋转一周需要的时间为5000, 所以可用来设置旋转的速度; 同时设置为无线循环模式, 并且添加一个插值器达到匀速旋转的目的, 最后调用pause()等函数动态控制动画状态, 如下:
文章图片
5. 进度条SeekBar功能实现
SeekBar的功能包括显示歌曲播放进度以及拖动SeekBar至任意位置来改变播放进度, 由于android已经封装好了这些函数, 所以直接调用就好了, 也比较好理解, 具体实现如下:
seekBar =
(SeekBar) findViewById(R.id.seekBar);
seekBar.setProgress(musicService.mediaPlayer.getCurrentPosition());
seekBar.setMax(musicService.mediaPlayer.getDuration());
seekBar.setProgress(musicService.mediaPlayer.getCurrentPosition());
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@
Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
musicService.mediaPlayer.seekTo(seekBar.getProgress());
}
}@
Override
public void onStartTrackingTouch(SeekBar seekBar) {}@
Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
6. 动态权限申请
参见Android动态权限申请
7. 判断程序是否处于后台
由于音乐需要支持后台播放, 再次由后台进入前台会对前台的事件功能等产生影响, 所以可以添加一个后台判断函数, 并添加一个标志来记录应用是否进入后台。
private boolean isApplicationBroughtToBackground() {
ActivityManager am =
(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<
ActivityManager.RunningTaskInfo>
tasks =
am.getRunningTasks(1);
if (!tasks.isEmpty()) {
ComponentName topActivity =
tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(getPackageName())) {
return true;
}
}
return false;
}
附上MainActivity和Service部分的完整代码
- MainActivity.java
package com.example.kx.temp;
import android.Manifest;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import com.google.android.gms.appindexing.Action;
import com.google.android.gms.appindexing.AppIndex;
import com.google.android.gms.common.api.GoogleApiClient;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.List;
public class MainActivity extends AppCompatActivity {private Button isPlay;
private Button stop;
private Button quit;
private ImageView coverImage;
// private ObjectAnimator animator;
private int flag =
0;
private TextView totalTime;
private TextView playingTime;
private TextView stateText;
private SeekBar seekBar;
private TextView pathText;
@
Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getOverflowMenu();
setContentView(R.layout.activity_main);
bindServiceConnection();
musicService =
new MusicService();
coverImage =
(ImageView) findViewById(R.id.coverImage);
musicService.animator =
ObjectAnimator.ofFloat(coverImage, "
rotation"
, 0, 359);
isPlay =
(Button) findViewById(R.id.isPlayButton);
isPlay.setOnClickListener(new myOnClickListener());
stop =
(Button) findViewById(R.id.stopButton);
stop.setOnClickListener(new myOnClickListener());
quit =
(Button) findViewById(R.id.quitButton);
quit.setOnClickListener(new myOnClickListener());
seekBar =
(SeekBar) findViewById(R.id.seekBar);
seekBar.setProgress(musicService.mediaPlayer.getCurrentPosition());
seekBar.setMax(musicService.mediaPlayer.getDuration());
totalTime =
(TextView) findViewById(R.id.totalTime);
playingTime =
(TextView) findViewById(R.id.playingTime);
stateText =
(TextView) findViewById(R.id.stateText);
pathText =
(TextView) findViewById(R.id.pathText);
String sdcard =
"
音乐文件的路径为:
"
+
Environment.getExternalStorageDirectory().getAbsolutePath().toString()+
"
/K.Will-Melt.mp3"
;
pathText.setText(sdcard);
}private MusicService musicService;
private SimpleDateFormat time =
new SimpleDateFormat("
mm:ss"
);
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(this, MusicService.class);
startService(intent);
bindService(intent, sc, this.BIND_AUTO_CREATE);
}public Handler handler =
new Handler();
public Runnable runnable =
new Runnable() {
@
Override
public void run() {isPlay.setOnClickListener(new myOnClickListener());
stop.setOnClickListener(new myOnClickListener());
quit.setOnClickListener(new myOnClickListener());
if(musicService.mediaPlayer.isPlaying()) {
stateText.setText("
Playing"
);
} else {
if (musicService.which.equals("
stop"
)){
stateText.setText("
Stop"
);
} else if (musicService.which.equals("
pause"
)){
stateText.setText("
Pause"
);
}
}
playingTime.setText(time.format(musicService.mediaPlayer.getCurrentPosition()));
totalTime.setText(time.format(musicService.mediaPlayer.getDuration()));
seekBar.setProgress(musicService.mediaPlayer.getCurrentPosition());
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@
Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
musicService.mediaPlayer.seekTo(seekBar.getProgress());
}
}@
Override
public void onStartTrackingTouch(SeekBar seekBar) {}@
Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
handler.postDelayed(runnable, 100);
}
};
@
Override
public void onPause(){
super.onPause();
if(isApplicationBroughtToBackground()) {
musicService.isReturnTo =
1;
Log.e("
b"
,"
后台中"
);
}
}@
Override
public void onRestart() {
super.onRestart();
musicService.isReturnTo =
1;
}@
Override
protected void onResume() {musicService.AnimatorAction();
verifyStoragePermissions(this);
if(musicService.mediaPlayer.isPlaying()) {
stateText.setText("
Playing"
);
} else {
if (musicService.which.equals("
stop"
)){
stateText.setText("
Stop"
);
} else if (musicService.which.equals("
pause"
)){
stateText.setText("
Pause"
);
}
}
seekBar.setProgress(musicService.mediaPlayer.getCurrentPosition());
seekBar.setMax(musicService.mediaPlayer.getDuration());
handler.post(runnable);
super.onResume();
Log.d("
hint"
, "
handler post runnable"
);
}private class myOnClickListener implements View.OnClickListener {
@
Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.isPlayButton:
changePlay();
musicService.playOrPause();
break;
case R.id.stopButton:
musicService.stop();
changeStop();
break;
case R.id.quitButton:
quit();
break;
default:
break;
}
}
}private void changePlay() {if(musicService.mediaPlayer.isPlaying()){
stateText.setText("
Pause"
);
isPlay.setText("
PLAY"
);
//animator.pause();
} else {
stateText.setText("
Playing"
);
isPlay.setText("
PAUSE"
);
}
}private void changeStop() {
stateText.setText("
Stop"
);
seekBar.setProgress(0);
//animator.pause();
}private void quit() {
musicService.animator.end();
handler.removeCallbacks(runnable);
unbindService(sc);
try {
finish();
System.exit(0);
} catch (Exception e) {
e.printStackTrace();
}
}@
Override
public void onStop() {
super.onStop();
}
@
Override
public void onDestroy() {
unbindService(sc);
super.onDestroy();
}//这里是在登录界面label上右上角添加三个点,
里面可添加其他功能
@
Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu;
this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.settings, menu);
return true;
}private void getOverflowMenu() {
try {
ViewConfiguration config =
ViewConfiguration.get(this);
Field menuKeyField =
ViewConfiguration.class.getDeclaredField("
sHasPermanentMenuKey"
);
if (menuKeyField !=
null) {
menuKeyField.setAccessible(true);
menuKeyField.setBoolean(config, false);
}
} catch (Exception e) {
e.printStackTrace();
}
}private boolean isApplicationBroughtToBackground() {
ActivityManager am =
(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<
ActivityManager.RunningTaskInfo>
tasks =
am.getRunningTasks(1);
if (!tasks.isEmpty()) {
ComponentName topActivity =
tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(getPackageName())) {
return true;
}
}
return false;
}// Storage Permissions
private static final int REQUEST_EXTERNAL_STORAGE =
1;
private static String[] PERMISSIONS_STORAGE =
{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
/**
* Checks if the app has permission to write to device storage
*
* If the app does not has permission then the user will be prompted to grant permissions
*
* @
param activity
*/
public static void verifyStoragePermissions(Activity activity) {
// Check if we have write permission
int permission =
ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission !=
PackageManager.PERMISSION_GRANTED) {
// We don'
t have permission so prompt the user
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
}
}
- Service
package com.example.kx.temp;
import android.Manifest;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.Environment;
import android.os.IBinder;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.animation.LinearInterpolator;
import java.io.File;
import java.util.List;
/**
* Created by kx on 2016/11/3.
*/
public class MusicService extends Service {
public final IBinder binder =
new MyBinder();
public class MyBinder extends Binder {
MusicService getService() {
return MusicService.this;
}
}public static int isReturnTo =
0;
public static MediaPlayer mediaPlayer =
new MediaPlayer();
public static ObjectAnimator animator;
public MusicService() {
initMediaPlayer();
}public void initMediaPlayer() {
try {
//String file_path =
"
/storage/0123-4567/K.Will-Melt.mp3"
;
String file_path =
Environment.getExternalStorageDirectory().getAbsolutePath()+
"
/K.Will-Melt.mp3"
;
//String file_path =
"
/data/K.Will-Melt.mp3"
;
mediaPlayer.setDataSource(file_path);
mediaPlayer.prepare();
mediaPlayer.setLooping(true);
// 设置循环播放
} catch (Exception e) {
e.printStackTrace();
}
}publicvoid AnimatorAction() {
if (mediaPlayer.isPlaying()) {
animator.setDuration(5000);
animator.setInterpolator(new LinearInterpolator());
// 均速旋转
animator.setRepeatCount(ValueAnimator.INFINITE);
// 无限循环
animator.setRepeatMode(ValueAnimator.INFINITE);
animator.start();
}
}
private int flag =
0;
public static String which =
"
"
;
public void playOrPause() {
flag+
+
;
if (flag >
=
1000) flag =
2;
which =
"
pause"
;
if(mediaPlayer.isPlaying()){
mediaPlayer.pause();
animator.pause();
} else {
mediaPlayer.start();
if ((flag =
=
1) || (isReturnTo =
=
1)) {
animator.setDuration(5000);
animator.setInterpolator(new LinearInterpolator());
// 均速旋转
animator.setRepeatCount(ValueAnimator.INFINITE);
// 无限循环
animator.setRepeatMode(ValueAnimator.INFINITE);
animator.start();
} else {
animator.resume();
}
}
}
public void stop() {
which =
"
stop"
;
animator.pause();
if(mediaPlayer !=
null) {
mediaPlayer.pause();
mediaPlayer.stop();
try {
mediaPlayer.prepare();
mediaPlayer.seekTo(0);
} catch (Exception e) {
e.printStackTrace();
}
}
}@
Override
public void onDestroy() {
mediaPlayer.stop();
mediaPlayer.release();
super.onDestroy();
}
/**
* onBind 是 Service 的虚方法,
因此我们不得不实现它。
* 返回 null,
表示客服端不能建立到此服务的连接。
*/
@
Override
public IBinder onBind(Intent intent) {
return binder;
}}
至此, 基于MediaPlayer的简单音乐播放器基本可以实现, 如有错误或疑问, 烦请指出。 【Android开发---MediaPlayer简单音乐播放器】完整项目参见github, 如君心情好, 别忘了加心哦。。。
祝君好心情~
推荐阅读
- Android adb常用指令
- Android艺术开发探索第四章——View的工作原理(下)
- Android 数据库读取数据显示 [5]
- Android关于Theme.AppCompat相关问题的深入分析(转)
- Android ImageView 正确使用姿势
- Android笔记——Application的作用
- Android studio关于真机调试DDMS中的data文件夹打不开的解决方法
- 灵活运用模板在Word2010中建文档的秘技_Word专区
- 在Word2010中保存文档的技巧汇总_Word专区