文章目录
一、参考文章
参考文章:Android音乐播放器的实现-简书
原文使用了service,也稍微看了一下。service大概就是下面这样的:但是我发现即使只用Activity,切出去之后歌曲的播放也不会停止,于是就暂时没有使用它,只用到了Activity,需要用到service的朋友可以看一下上面那篇文章。
service是Android中实现程序后台的解决方案,不依赖任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,service还能保持运行。
二、功能说明
实现了播放、暂停、停止、下一首歌、上一首歌和进度条的功能,可以拖动进度条来改变歌曲的进度。
界面就像下面这样:丑丑的
三、重要代码说明
1、存储权限读取
① 首先调用checkSelfPermission进行权限检查,如果无权限则使用requestPermissions进行权限申请,其中PackageManager.PERMISSION_GRANTED 表示有权限, PackageManager.PERMISSION_DENIED 表示无权限。
if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
//这里会调用后面的onRequestPermissionResult
}else{
initMediaPlayer(0);
}
② requestPermissions方法进行权限申请后会自动调用onRequestPermissionsResult(),覆写onRequestPermissionsResult()
@Override
//拒绝权限获取则直接关闭程序
public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults){
switch (requestCode){
case 1:
{
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
initMediaPlayer(0);
}else{
Toast.makeText(this,"拒绝权限将无法使用程序",Toast.LENGTH_SHORT).show();
finish();
}
break;
}
default:
break;
}
}
③ 在AndroidManifest.xml文件中注明申请了什么权限
注意uses-permission要作为manifest的直接孩子
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
2、指定歌曲路径,初始化MediaPlayer组件并使其进入准备状态
下面这段代码你没看错,就是很粗暴地自己在内部存储根目录下建一个叫做MyTestMusic的文件夹,然后放四首歌进去,再把这几首歌都作为歌曲的路径。
File pFile = Environment.getExternalStorageDirectory();//SD卡根目录
//歌曲路径
private String[] musicPath = new String[]{
pFile + "/MyTestMusic/想自由_林宥嘉.flac",
pFile + "/MyTestMusic/Adele-Someone Like You.ape",
pFile + "/MyTestMusic/Taylor Swift - Love Story.mp3",
pFile + "/MyTestMusic/感同身受_林宥嘉.flac"
};
private void initMediaPlayer(int musicIndex){
try {
//File file = new File(pFile,"Adele-Someone Like You.ape");
mediaPlayer.setDataSource(musicPath[musicIndex]);//指定音频文件路径
mediaPlayer.prepare();//让MediaPlayer进入到准备状态
}catch(Exception e){
e.printStackTrace();
}
}
3、进度条功能
① 在initMediaPlayer中加入对进度条最大长度的设置,并通过setOnSeekBarChangeListener来完成用户拖动进度条时改变歌曲进度的功能。为什么加在这里呢,因为每换一首歌seekBar的长度都要发生变化。
//这个要放在指定音频文件路径之后
seekBar.setMax(mediaPlayer.getDuration());
//拖动进度条时应该发生的事情
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//如果不判断是否来自用户操作进度条,会不断执行下面语句块里面的逻辑
if(fromUser){
//seekBar.getProgress()为进度条拖到的地方
mediaPlayer.seekTo(seekBar.getProgress());
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
② 更新UI,获取歌曲进度并在进度条上展现,并每隔一秒跳一个数字
主线程创建handler,在子线程中通过handler的post(Runnable)方法更新UI信息。
private Handler myHandler = new Handler();
……
//更新UI
private Runnable updateUI = new Runnable() {
@Override
public void run() {
//获取歌曲进度并在进度条上展现
seekBar.setProgress(mediaPlayer.getCurrentPosition());
//获取播放位置
timeTextView.setText(time.format(mediaPlayer.getCurrentPosition()) + "s");
myHandler.postDelayed(updateUI,1000);
}
};
③ 在onCreate函数的最后:
myHandler.post(updateUI);
四、用到的MediaPlayer的方法总结
重要的功能就前面那些了,其他的开始什么都是很容易地调用一下就可以了,下面总结一下MediaPlayer有哪些方法好了。
方法 | 功能 |
---|---|
setDataSource() | 指定音频文件路径 |
prepare() | 让MediaPlayer进入到准备状态 |
getDuration() | 获取歌曲长度 |
getCurrentPosition() | 获取现在播放到的位置 |
seekTo(int msec) | 播放到指定位置 |
isPlaying() | 是否正在播放 |
start() | 开始播放 |
pause() | 暂停播放 |
reset() | 应该是有些停止播放的意味,后面一般重新使MediaPlayer进入准备状态 |
release() | 释放资源 |
五、完整代码
1、activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:layout_marginVertical="30dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginHorizontal="20dp">
<Button
android:id="@+id/play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="play" />
<Button
android:id="@+id/pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="pause"
android:layout_marginLeft="40dp"/>
<Button
android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="stop"
android:layout_marginLeft="20dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginHorizontal="30dp"
android:layout_marginVertical="30dp">
<Button
android:id="@+id/previous"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="previous" />
<Button
android:id="@+id/next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="next"
android:layout_marginLeft="120dp"/>
</LinearLayout>
<SeekBar
android:layout_marginTop="20dp"
android:id="@+id/seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:text="当前进度:"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
2、MainActivity.java
package com.example.yogi.mymusicplayer;
import android.Manifest;
import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.os.Environment;
import android.os.Handler;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.text.SimpleDateFormat;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
//主线程创建handler,在子线程中通过handler的post(Runnable)方法更新UI信息。
private Handler myHandler = new Handler();
private MediaPlayer mediaPlayer = new MediaPlayer();
private SeekBar seekBar;
private TextView timeTextView;
//进度条下面的当前进度文字,将毫秒化为m:ss格式
private SimpleDateFormat time = new SimpleDateFormat("m:ss");
private int i = 0;//当前歌曲序号
File pFile = Environment.getExternalStorageDirectory();//SD卡根目录
//歌曲路径
private String[] musicPath = new String[]{
pFile + "/MyTestMusic/想自由_林宥嘉.flac",
pFile + "/MyTestMusic/Adele-Someone Like You.ape",
pFile + "/MyTestMusic/Taylor Swift - Love Story.mp3",
pFile + "/MyTestMusic/感同身受_林宥嘉.flac"
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button play = findViewById(R.id.play);
Button stop = findViewById(R.id.stop);
Button pause = findViewById(R.id.pause);
Button nextMusic = findViewById(R.id.next);
Button preMusic = findViewById(R.id.previous);
play.setOnClickListener(this);
stop.setOnClickListener(this);
pause.setOnClickListener(this);
nextMusic.setOnClickListener(this);
preMusic.setOnClickListener(this);
seekBar = findViewById(R.id.seekbar);
timeTextView = findViewById(R.id.text1);
//运行时权限处理,动态申请WRITE_EXTERNAL_STORAGE权限
//PackageManager.PERMISSION_GRANTED 表示有权限, PackageManager.PERMISSION_DENIED 表示无权限
if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
//这里会调用后面的onRequestPermissionResult
}else{
initMediaPlayer(0);
}
myHandler.post(updateUI);
}
private void initMediaPlayer(int musicIndex){
try {
//File file = new File(pFile,"Adele-Someone Like You.ape");
mediaPlayer.setDataSource(musicPath[musicIndex]);//指定音频文件路径
mediaPlayer.prepare();//让MediaPlayer进入到准备状态
}catch(Exception e){
e.printStackTrace();
}
//这个要放在指定音频文件路径之后
seekBar.setMax(mediaPlayer.getDuration());
//拖动进度条时应该发生的事情
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//如果不判断是否来自用户操作进度条,会不断执行下面语句块里面的逻辑
if(fromUser){
mediaPlayer.seekTo(seekBar.getProgress());
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
@Override
//拒绝权限获取则直接关闭程序
public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults){
switch (requestCode){
case 1:
{
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
initMediaPlayer(0);
}else{
Toast.makeText(this,"拒绝权限将无法使用程序",Toast.LENGTH_SHORT).show();
finish();
}
break;
}
default:
break;
}
}
@Override
public void onClick(View v){
switch (v.getId()){
case R.id.play:
if(!mediaPlayer.isPlaying()){
mediaPlayer.start();
}
break;
case R.id.pause:
if(mediaPlayer.isPlaying()){
mediaPlayer.pause();
}
break;
case R.id.stop:
if(mediaPlayer.isPlaying()){
mediaPlayer.reset();
initMediaPlayer(i);
}
break;
case R.id.next:
playNextMusic();
break;
case R.id.previous:
playPreMusic();
break;
default:
break;
}
}
private void playNextMusic(){
if(mediaPlayer != null && i < 4 && i >=0){
mediaPlayer.reset();//没有reset会报IllegalStateException
switch (i){
case 0: case 1: case 2:
initMediaPlayer(i+1);
i = i + 1;
case 3:
initMediaPlayer(0);
}
if(!mediaPlayer.isPlaying()){
mediaPlayer.start();
}
}
}
private void playPreMusic(){
if(mediaPlayer != null && i < 4 && i >=0){
mediaPlayer.reset();//没有reset会报IllegalStateException
switch (i){
case 1: case 2: case 3:
initMediaPlayer(i-1);
i = i - 1;
case 0:
initMediaPlayer(3);
}
if(!mediaPlayer.isPlaying()){
mediaPlayer.start();
}
}
}
//更新UI
private Runnable updateUI = new Runnable() {
@Override
public void run() {
//获取歌曲进度并在进度条上展现
seekBar.setProgress(mediaPlayer.getCurrentPosition());
//获取播放位置
timeTextView.setText(time.format(mediaPlayer.getCurrentPosition()) + "s");
myHandler.postDelayed(updateUI,1000);
}
};
//释放资源
protected void onDestroy(){
super.onDestroy();
//handler发送是定时1000s发送的,如果不关闭,MediaPlayer release了还在getCurrentPosition就会报IllegalStateException错误
myHandler.removeCallbacks(updateUI);
if(mediaPlayer != null){
mediaPlayer.stop();
mediaPlayer.release();
}
}
}
3、不要忘记AndroidManifect也需要写明申请什么权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.yogi.mymusicplayertest">
……
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>