Android多媒体应用开发之MP3音乐播放器

一,MediaPlayer类概述
MediaPlayer是ANdroid自带的多媒体库,可用于实现音频操作,若结合SurfaceView和VideoView等控件可是实现视频的播放。

1,创建MediaPlayer对象,并装载音频文件
①可以直接使用new的方式

MediaPlayer mp=new MediaPlayer();

此时,需要调用setDataSource()方法指定要播放的资源文件,调用prepare(),调用start()进行播放。

②使用create的方式

MediaPlayer mp=MediaPlayer.create(this,T.raw.test);

此时,不用调用setDataSource(),也不需要调用prepare(),可直接调用start()函数进行播放。

2,设置要播放的文件
通过setDataSource()方法

①void setDataSource(String path); 指定装载path路径所代表的文件。

②void setDataSource(FileDescriptor fd, long offset, long length); 指定装载fd所代表的文件中从offset开始、长度为length的文件内容。

③void setDataSource(FileDescroptor fd); 指定装载fd所代表的文件。

④void setDateSource(Context context, Uri uri); 指定装载Uri代表的文件。

注意:在使用setDataSource()方法装载音频文件后,实际上MediaPlayer并真正装载该音频文件,还需要调用MediaPlayer的prepare()方法真正装载音频文件。

3,播放的文件的三个来源
(1)播放应用中事先自带的resource资源
*res文件夹与assert文件夹的区别
Android中子源分为两种,
res下的可编译的资源文件,这种资源文件会在R中自动生成该资源文件的ID,直接通过R.xxx.id访问;res/raw不可以有目录结构
assets文件夹下的原生资源文件,不会被R文件编译;可以有目录结构,既是可以在assets目录下在建立文件夹。
*
Ⅰ,播放应用的资源文件
①调用MediaPlayer的create(Context context , int resid)方法加载指定资源文件

②调用MediaPlayer的start()、pause()、stop()等方法控制播放即可

MediaPlayer.create(this,R.raw.song);
Uri uri=Uri.parse("android.resource://"+this.getPackageName()+"/"+R.raw.da);
player=new MediaPlayer();

player.setDataSource(this,uri);
player.perpare();

Ⅱ,播放应用的原始资源文件
①,调用Activity中的getAssets()方法获取应用的AssetManager

②,调用AssetManager对象的openFd(STring name)方法打开指定的原生资源,该方法返回一个AssetFileDescriptor对象。

③,调用AssetFileDescriptor的getFileDescriptor()和getLength()方法来获取音频文件的FileDescriptor、开始位置、长度

④,创建MediaPlayer对象,并调用MediaPlayer对象的setDataResource(FileDescriptor fd,long offeset,long length)方法来装在音频资源

⑤,调用MediaPlayer对象的prepare()方法准备音频

⑥,调用MediaPlayer的start(),pause()、stop()等方法控制播放即可。

🔺(2)存储在SD卡或其他文件路径下的媒体文件
①创建MediaPlayer对象,并调用MediaPlayer对象的setDataSource(String path)方法装载指定音频文件

②调用MediaPlayer对象的prepare()方法准备音频

③调用MediaPlayer的start()、stop()等方法控制播放即可

player = new MediaPlayre();
player.setDataSource("/sdcard/pm.mp3");
//或
//paler.setDataSource(Environment.getExternalStorageDirectory()+"/pm.mp3);
player.prepare();

注意:对sdcard的访问需要在AndroidManifest.xml中设置相应的权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>

在安卓23.0版本以上,还需要动态申请权限

//判断是否6.0以上的手机
if(Build.VERSION.SDK_INT>=23){
	//判断是否有这个权限
	int permmission=ActivityCompat.checkSelfPermission(this,Manifest.permission.READ_EXTERNAL_STORAGE);
	//checkSelfPremission检查对应的权限是否打开,如果是PackageManager.PERMISSION_GRANTED表示打开
	if(permission!=PackageManager.PERMISSION_GRANTED){
		//请求权限
		ActivityCompat.requestPermissions(this,new String[]{Manifes.permission.READ_EXTERNAL_STORAGE},ECTERNAL_STORAGE_REQ_CODE);
}

(3)网络上的媒体文件
①直接使用create(Context context ,Uri uri)

MediaPlayer mp=MediaPlayer.create(this,Uri.parse(http://www.test.cn/music/m1.mp3))

②调用setDataSource(Context context,Uri uri)

mp.setDataSource(this,Uri.parse("htp://www.tst.cn/music/m1.mp3"));

此时,需要在AndroidManifest.xml中授权程序访问网络的权限

<users-permission android:name="android.permission.INTERNET"/>

4,对播放器的主要控制方法
①prepare():同步

同步的为播放器的回放做准备,设置数据源和显示表面后调用;对于文件,可以调用prepare(),它会阻塞,直到MediaPlayer准备好播放为止。

②prepareAsync():异步

异步的为播放器的回放做准备。设置好数据源和显示表面后调用。对于流,应该调用prepareAsync(),它会立即返回,而不是阻塞,直到缓冲了足够的数据。

注意:prepare()方法,是将资源同步缓存到内存中,一般加载本地较小的资源使用。如果较大的资源或网络资源建议使用prepareAsync()

▲对于异步装载考虑是否能在资源装载完成后才可以操作
需要设置监听事件setOnPreparedListener();来通知MediaPlayer资源已经获取到了,然后实现onPrepared(MediaPlayre mp)方法,在里面启动MediaPlayer

viderView.setOnPreparedListener(
	new OnpreparedListener(){
		public void onPrepared(MediaPlayer mp){
			mp.play();
		}
	}
);

5,MediaPlayer的方法
start(): 开始播放或恢复已经暂停的音频的播放

stop(): 停止正在播放的音频

pause(): 暂停正在播放的音频

void seekTo(int msec): 设置当前MediaPlayre的播放位置,可以让播放器从指定位置开始播放,单位是毫秒。

int getDuration(): 获取流媒体的总播放时长,单位是毫秒

int getCurrentPosition():获取当流媒体的播放位置,单位是毫秒

void setLooping(boolean looping); 设置是否循环播放。播放完毕后,不会回调OnCompletionListener,而是从头播放当前音频

boolean isLooping(); 判断是否循环播放

boolean isPlaying(); 判断是否正在播放

void release(); 回收流媒体资源

void setAudioStreamType(int streamtype); 设置播放流媒体类型

void setWakeMode(Context context,int mode); 设置CPU唤醒的状态

setNextMediaPlayer(MedaiPlayer next); 设置当前流媒体播放完毕,下一个播放的MediaPlayer

6,MediaPlayer的监听事件
通常在新建一个MedaiPlayer实体后,会对给它增加需要的监听事件

MediaPlayer.onPreparedListener: MediaPlayer进入准备完毕的状态出发,表示流媒体可以开始播放了

MediaPlayer.OnSeekCompleteListener: 调用MediaPlayer的seekTo方法后,MediaPlayer会跳转到媒体指定的位置,当跳转完成时触发。(注意:seekTo不能精确的跳转,它的跳转点必须是媒体资源的关键帧)

MediaPlayer.OnBufferingUpdateListener: 网络上的媒体资源缓存进度更新的时候被触发,比如在加载网络音频的时候,常用这个监听器来监听缓存进度。

MediaPlayer.OnCompletionListener: 媒体播放完毕时会触发。(由于playere占有的系统资源比较大,因此在播放结束后,就应该掉哟ing该方法,把player占有的资源给释放掉。当OnErrorLister返回false,或MediaPlayer没有设置OnErrorListener时,这个监听会发触发)

MediaPlayer.OnvideoSizeChangeListener: 视频宽高发生改变的时候会触发。当所设置的媒体资源没有视频图像、MediaPlayer没有设置展示的holder或者视频大小还没有测量出来的时候,获取宽高得到的都是0

MediaPlayer.OnErrorListerner: MediaPlayer出错时会触发,无论是播放过程中出错,还是准备过程中出错,都会触发。

6,MediaPlayer的状态图和生命周期

在这里插入图片描述

二、MP3音乐播放器
1,功能
从SD卡中读取MP3文件并加载到播放列表中,控制音乐的播放和停止,实现对播放列表中音乐的上一首、下一首的切换,播放完成时自动给跳转到下一首播放,当播放到最后一首音乐时自动切换到第一首进行播放。通过进度表跟踪播放进度和拖动进度条拖动进度。

2,代码

MainActivity.java文件:

package com.example.mp3;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.Toast;

import androidx.core.app.ActivityCompat;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends Activity {//默认继承AppCompatActivity,会报错

    private MediaPlayer player=new MediaPlayer();
    private List<String> musicList=new ArrayList<>();//保存SD卡中的所有音乐文件的名称
    private int currentListItem=0;//当前正在播放音乐的下表
    private boolean isPlaying;//表示当前是否正在播放音乐
    private SeekBar seekBar;
    private Timer timer;
    private  TimerTask task;
    private int currentposition;//保存当前播放的音乐的位置

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        seekBar=findViewById(R.id.sb_seekbar);

        //动态申请权限
        final int permission= ActivityCompat.checkSelfPermission(this,
                Manifest.permission.READ_EXTERNAL_STORAGE);
        if(permission!= PackageManager.PERMISSION_GRANTED){
            //请求权限
            ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},10);
        }
        //获取sdcard/Music目录下所有的音乐文件并保存至musicList
        //获取这个目录下所有的文件
        // 得到sd卡内Music文件夹的路径
        String filePath = Environment.getExternalStorageDirectory().toString() + File.separator + "Music";
        // 得到该路径文件夹下所有的文件
        File fileAll = new File(filePath);
        File[] files = fileAll.listFiles();
        //判断files是否有音乐文件
        if(files!=null)
        {
            //获得所有音乐文件的路径
            for(File f:files)
                musicList.add(f.getName());
        }
        //获得ListView
        ListView  lv_list=findViewById(R.id.lv_list);
        //将musicList包装成ArrayAdapter
        ArrayAdapter adapter=new ArrayAdapter<>(this,R.layout.array_item,musicList);//需要<String>,否则警告
        //为ListVeiw设置Adapter
        lv_list.setAdapter(adapter);
        //设置ListVIew的事件监听器
        lv_list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                currentListItem=position;
                play();
            }
        });


        //通过保持在MusicLst中的路径找到音乐文件并播放
        File file = new File(filePath+"/"+musicList.get(currentListItem));

        //判断音频文件是否存在,
        //进行播放设置
        if(file.exists()){
            try{
                player.setDataSource(file.getAbsolutePath());
                player.prepare();
                player.start();
                followSeekBar();
            }
            catch(Exception e){
                Toast.makeText(MainActivity.this,"播放出dfdf错!",Toast.LENGTH_SHORT).show();
            }

        }
        else{
            //提示
            Toast.makeText(MainActivity.this,"音乐不存在!",Toast.LENGTH_SHORT).show();
        }

        //MedaiPlayer完成事件监听器,当前音乐播放完成后制动开始播放下一首
        player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
               //当播放结束时取消计时
                timer.cancel();
                task.cancel();
                //播放下一首
                currentListItem=(currentListItem+1) % musicList.size();//播放到最后一首后,从头开始
                play();

            }
        });
        //上一首按钮的事件监听器
        Button bt_frontMusic=findViewById(R.id.bt_frontMusic);
        bt_frontMusic.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                frontMusic();//播放上一首
            }
        });
        //下一首按钮的事件监听器
        Button bt_nextMusic=findViewById(R.id.bt_nextMusic);
        bt_nextMusic.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                nextMusic();//播放下一首
            }
        });
        //播放暂停按钮的事件监听器
        Button bt_playPause=findViewById(R.id.bt_playPause);
        bt_playPause.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                playMusic();
            }
        });

        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) {
                currentposition=seekBar.getProgress();
                player.seekTo(currentposition);
            }
        });
    }//onCreate

    private void play(){
        try {
            player.reset();
            player.setDataSource(Environment.getExternalStorageDirectory().toString() + File.separator + "Music"+ File.separator+musicList.get(currentListItem));
            player.prepare();
            player.start();
            isPlaying=true;//正在播放标记置为true

            followSeekBar();

        }catch (Exception e){
            Toast.makeText(MainActivity.this,"播放出错!",Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(player.isPlaying()){
            player.stop();
        }
        player.release();
    }

    //播放上一首
    private void frontMusic()
    {
        if(currentListItem==0){
            Toast.makeText(MainActivity.this,"已经是第一首了!",Toast.LENGTH_LONG).show();
        }
        else{
            --currentListItem;
            play();
        }
    }
    //播放下一首
    private void nextMusic(){
        if(currentListItem+1==musicList.size()){
            Toast.makeText(MainActivity.this,"已经是最后一首了!",Toast.LENGTH_LONG).show();
        }
        else{
            ++currentListItem;
            play();
        }
    }
    //播放暂停
    public void playMusic(){
        if(isPlaying)
        {
            player.pause();
            isPlaying=false;
        }else{
            player.start();
            isPlaying=true;
        }
    }

    private void followSeekBar()
    {
        //获取当前歌曲的总时长
        int duration=player.getDuration();
        seekBar.setMax(duration);
        //计时器对象
        timer=new Timer();
        task=new TimerTask(){
            public void run(){
                //开启线程定时获取当前播放进度
                currentposition=player.getCurrentPosition();
                seekBar.setProgress(currentposition);
            }
        };
        timer.schedule(task,300,500);
    }
}

activity_main.xml文件:

```xml
<?xml version="1.0" encoding="utf-8"?>
<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:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <TextView android:id="@+id/tv_musicList"
            android:layout_width="match_parent"
            android:layout_height="20pt"
            android:text="歌曲列表"
            android:textSize="12pt"
            android:gravity="center_horizontal"/>

        <ListView
            android:id="@+id/lv_list"
            android:layout_width="match_parent"
            android:layout_height="560dp"
            android:divider="#666"
            android:dividerHeight="1dp"
            android:headerDividersEnabled="false" />
        <SeekBar android:id="@+id/sb_seekbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:max="100"
            android:progress="0"
            />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/LL"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="bottom"
        android:orientation="horizontal">

        <Button
            android:id="@+id/bt_frontMusic"
            style="?android:attr/buttonBarButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="上一首"
            android:textSize="10pt"
            android:layout_weight="1"/>

        <Button
            android:id="@+id/bt_playPause"
            style="?android:attr/buttonBarButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="播放/暂停"
            android:textSize="10pt" />

        <Button
            android:id="@+id/bt_nextMusic"
            style="?android:attr/buttonBarButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="下一首"
            android:textSize="10pt" />
    </LinearLayout>
</LinearLayout>
array_item.xml文件:

```xml
<?xml version="1.0" encoding="utf-8"?>
<TextView android:id="@+id/TextView"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="12pt"
    android:padding="5dp"
    android:shadowColor="#89a"
    android:shadowDx="4"
    android:shadowDy="4"
    android:shadowRadius="2"
    />

因为需要读取SD卡,所以需要在AndroidManifest.xml中配置相应权限:

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE "></uses-permission>


本代码还存在着较多bug,比如当SD卡没有音乐文件的时候会报错,所以需要进行完整的系统测试和bug修改,才能正常的时候。

在运行代码时,在如下代码中

//获取sdcard/Music目录下所有的音乐文件并保存至musicList
//获取这个目录下所有的文件
// 得到sd卡内Music文件夹的路径
String filePath = Environment.getExternalStorageDirectory().toString() + File.separator + "Music";
// 得到该路径文件夹下所有的文件
File fileAll = new File(filePath);
File[] files = fileAll.listFiles();

files对象一直为空,经过不断检查和debug依然没有发现代码存在的问题,最后无意间换上Genymotion的模拟器问题直接消失,最终确定应该是Android Studio自带的模拟器的问题。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值