一个基于Android开发的简单的音乐播放器

    xiaoxiao2023-11-13  169

    一个基于Android开发的简单的音乐播放器

    记得当时老师让我们写因为播放器时,脑子一头雾水,网上杂七杂八的资料也很少有用,因此索性就自己写一篇,希望对有缘人有用。

    效果图

    首先先上效果图,原本我是拍了个视频的,但是没法上传,因此就只能上个效果图,图中有的功能都已实现。

    编写流程

    1.因为我是用真机调试的,所以先在手机的根目录下创建一个二级目录,往里面添加mp3文件。 2.使用List链表数组存放mp3文件的路径 3.使用MediaPlayer()实例化一个对象,使用该对象的play,reset,stop等方法。 4.在MainActivity中调用,同时实例化一个handler来实现拖动seekbar的滑杆改变播放进度的功能。

    实现流程

    第一点就不说了,就是在自己手机的根目录下新建一个目录,往里面加mp3文件。 第二点相关代码:

    public MusicService() { super(); player = new MediaPlayer();//实例化一个多媒体对象 musicList = new ArrayList<String>();//实例化一个List链表数组 try { File MUSIC_PATH = new File(PATH, "得到");//获取根目录的二级目录Music if (MUSIC_PATH.listFiles(new MusicFilter()).length > 0) { for (File file : MUSIC_PATH.listFiles(new MusicFilter())) { musicList.add(file.getAbsolutePath()); } } } catch (Exception e) { Log.i("TAG", "读取文件异常"); } }

    不难看出,我是将mp3文件的路劲存放在musicList这个List数组里的。这个之后就会用到。

    第三点是实现play、stop、pause等基础功能功能。在这里我使用了MediaPlayer类的基本方法。比如在play中,我先从List链表数组中获取当前播放音乐的路径,然后使用prepare()(准备播放)和start()(开始播放)方法,实现play方法的功能。

    public void play() { try { player.reset(); //重置多媒体 String dataSource = musicList.get(songNum);//得到当前播放音乐的路径 setPlayName(dataSource);//截取歌名 // 指定参数为音频文件 player.setAudioStreamType(AudioManager.STREAM_MUSIC); player.setDataSource(dataSource);//为多媒体对象设置播放路径 player.prepare();//准备播放 player.start();//开始播放 //setOnCompletionListener 当当前多媒体对象播放完成时发生的事件 player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { public void onCompletion(MediaPlayer arg0) { next();//如果当前歌曲播放完毕,自动播放下一首. } }); } catch (Exception e) { Log.v("MusicService", e.getMessage()); } }

    注解写得很详细,其他的功能与之类似,因此不再赘述。

    第四点是我认为比较难得点,我查了挺多资料,最后决定用实例化一个handler来实现拖动seekbar的滑杆改变播放进度的功能。 那我为什么会选择他呢? 其实只是这样的:默认情况下,Handler接受的是当前线程下的消息循环实例(使用Handler(Looper looper)、Handler(Looper looper, Handler.Callback callback)可以指定线程),同时一个消息队列可以被当前线程中的多个对象进行分发、处理(在UI线程中,系统已经有一个Activity来处理了,你可以再起若干个Handler来处理)。 在实例化Handler的时候,Looper可以是任意线程的,只要有Handler的指针,任何线程也都可以sendMessage。Handler对于Message的处理不是并发的。一个Looper 只有处理完一条Message才会读取下一条,所以消息的处理是阻塞形式的(handleMessage()方法里不应该有耗时操作,可以将耗时操作放在其他线程执行,操作完后发送Message(通过sendMessges方法),然后由handleMessage()更新UI)。

    没看懂也没关系,直接看代码最有用。

    class VideoThreed extends Thread { @Override public void run() { int position, mMax, sMax; while (!Thread.currentThread().isInterrupted()) { if (musicService.player != null && musicService.player.isPlaying()) { position = musicService.getCurrentProgress();//得到当前歌曲播放进度(秒) mMax = musicService.player.getDuration();//最大秒数 sMax = seekBar.getMax();//seekBar最大值,算百分比 Message m = handler.obtainMessage();//获取一个Message m.arg1 = position * sMax / mMax;//seekBar进度条的百分比 m.arg2 = position; m.what = UPDATE; handler.sendMessage(m); // handler.sendEmptyMessage(UPDATE); try { Thread.sleep(1000);// 每间隔1秒发送一次更新消息 } catch (InterruptedException e) { e.printStackTrace(); } } } }} //实例化一个handler对象 handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); //更新UI int mMax = musicService.player.getDuration();//最大秒数 if (msg.what == UPDATE) { try { seekBar.setProgress(msg.arg1); txtInfo.setText(setPlayInfo(msg.arg2 / 1000, mMax / 1000)); } catch (Exception e) { e.printStackTrace(); } } else { seekBar.setProgress(0); txtInfo.setText("播放已经停止"); } } };

    好了,大概就是这样子,接下来是源码

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>

    <LinearLayout android:layout_width="wrap_content" android:layout_height="400dp" android:orientation="vertical"> <ListView android:id="@+id/lv1" android:layout_width="wrap_content" android:layout_height="wrap_content"></ListView> </LinearLayout> <SeekBar android:id="@+id/sb" android:layout_width="match_parent" android:layout_height="30dp" android:maxHeight="2dp" android:minHeight="2dp" android:paddingBottom="3dp" android:paddingLeft="12dp" android:max="200" android:paddingRight="12dp" android:paddingTop="3dp" /> <LinearLayout android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"> <Button android:id="@+id/btn_last" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="上一首"/> <Button android:id="@+id/btn_next" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="下一首 "/> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"> <Button android:id="@+id/btn_star" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="开始"/> <Button android:id="@+id/btn_pause" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="暂停"/> <Button android:id="@+id/btn_stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="停止"/> <Button android:id="@+id/btn_replay" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="重播"/> </LinearLayout>

    MusicService

    package com.jack.musicplayer; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Environment; import android.util.Log; import java.io.File; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.List; public class MusicService { private static final File PATH = Environment.getExternalStorageDirectory();// 获取SD卡总目录。 public List<String> musicList;// 存放找到的所有mp3的绝对路径。 public MediaPlayer player; // 定义多媒体对象 public int songNum; // 当前播放的歌曲在List中的下标,flag为标致 public String songName; // 当前播放的歌曲名 class MusicFilter implements FilenameFilter { public boolean accept(File dir, String name) { return (name.endsWith(".mp3"));//返回当前目录所有以.mp3结尾的文件 } } public MusicService() { super(); player = new MediaPlayer();//实例化一个多媒体对象 musicList = new ArrayList<String>();//实例化一个List链表数组 try { File MUSIC_PATH = new File(PATH, "得到");//获取根目录的二级目录Music if (MUSIC_PATH.listFiles(new MusicFilter()).length > 0) { for (File file : MUSIC_PATH.listFiles(new MusicFilter())) { musicList.add(file.getAbsolutePath()); } } } catch (Exception e) { Log.i("TAG", "读取文件异常"); } } public void setPlayName(String dataSource) { File file = new File(dataSource);//假设为D:\\dd.mp3 String name = file.getName();//name=dd.mp3 int index = name.lastIndexOf(".");//找到最后一个 . songName = name.substring(0, index);//截取为dd } public void play() { try { player.reset(); //重置多媒体 String dataSource = musicList.get(songNum);//得到当前播放音乐的路径 setPlayName(dataSource);//截取歌名 // 指定参数为音频文件 player.setAudioStreamType(AudioManager.STREAM_MUSIC); player.setDataSource(dataSource);//为多媒体对象设置播放路径 player.prepare();//准备播放 player.start();//开始播放 //setOnCompletionListener 当当前多媒体对象播放完成时发生的事件 player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { public void onCompletion(MediaPlayer arg0) { next();//如果当前歌曲播放完毕,自动播放下一首. } }); } catch (Exception e) { Log.v("MusicService", e.getMessage()); } } //继续播放 public void goPlay(){ int position = getCurrentProgress(); player.seekTo(position);//设置当前MediaPlayer的播放位置,单位是毫秒。 try { player.prepare();// 同步的方式装载流媒体文件。 } catch (Exception e) { e.printStackTrace(); } player.start(); } // 获取当前进度 public int getCurrentProgress() { if (player != null & player.isPlaying()) { return player.getCurrentPosition(); } else if (player != null & (!player.isPlaying())) { return player.getCurrentPosition(); } return 0; } public void next() { songNum = songNum == musicList.size() - 1 ? 0 : songNum + 1; play(); } public void last() { songNum = songNum == 0 ? musicList.size() - 1 : songNum - 1; play(); } // 暂停播放 public void pause() { if (player != null && player.isPlaying()){ player.pause(); } } public void stop() { if (player != null && player.isPlaying()) { player.stop(); player.reset(); } } }

    代码本身就有详细的注解,相信你一定能触类旁通。

    MainActivity

    package com.jack.musicplayer; import android.Manifest; import android.content.pm.PackageManager; import android.media.MediaPlayer; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; import java.io.File; public class MainActivity extends AppCompatActivity { int flag = 1;//设置一个标志,供点击“开始/暂停”按钮使用 private Button btnStart, btnStop, btnNext, btnLast,btnPause,btnRePlay; private TextView txtInfo; private ListView listView; private SeekBar seekBar; private MusicService musicService = new MusicService(); private Handler handler;// 处理改变进度条事件 int UPDATE = 0x101; private boolean autoChange, manulChange;// 判断是进度条是自动改变还是手动改变 private boolean isPause;// 判断是从暂停中恢复还是重新播放; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); class VideoThreed extends Thread { @Override public void run() { int position, mMax, sMax; while (!Thread.currentThread().isInterrupted()) { if (musicService.player != null && musicService.player.isPlaying()) { position = musicService.getCurrentProgress();//得到当前歌曲播放进度(秒) mMax = musicService.player.getDuration();//最大秒数 sMax = seekBar.getMax();//seekBar最大值,算百分比 Message m = handler.obtainMessage();//获取一个Message m.arg1 = position * sMax / mMax;//seekBar进度条的百分比 m.arg2 = position; m.what = UPDATE; handler.sendMessage(m); // handler.sendEmptyMessage(UPDATE); try { Thread.sleep(1000);// 每间隔1秒发送一次更新消息 } catch (InterruptedException e) { e.printStackTrace(); } } } } } //实例化一个handler对象 handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); //更新UI int mMax = musicService.player.getDuration();//最大秒数 if (msg.what == UPDATE) { try { seekBar.setProgress(msg.arg1); txtInfo.setText(setPlayInfo(msg.arg2 / 1000, mMax / 1000)); } catch (Exception e) { e.printStackTrace(); } } else { seekBar.setProgress(0); txtInfo.setText("播放已经停止"); } } }; try { setListViewAdapter();//添加文件名字 } catch (Exception e) { Log.i("TAG", "读取信息失败"); } btnStart = (Button) findViewById(R.id.btn_star); btnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { if (flag == 1) { musicService.play(); flag++; } } catch (Exception e) { Log.i("LAT", "开始异常!"); } } }); btnRePlay=(Button)findViewById(R.id.btn_replay); btnRePlay.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View view){ try{ musicService.stop(); seekBar.setProgress(0); musicService.play(); } catch (Exception e) { Log.i("LAT", "暂停异常!"); } } }); btnPause=(Button)findViewById(R.id.btn_pause); btnPause.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View view){ try{ if (musicService.player.isPlaying()){ musicService.pause(); btnPause.setText("继续"); } else{ musicService.goPlay(); btnPause.setText("暂停"); } } catch (Exception e) { Log.i("LAT", "暂停异常!"); } } }); btnStop = (Button) findViewById(R.id.btn_stop); btnStop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { musicService.stop(); flag = 1;//当点击停止按钮时,flag置为1 seekBar.setProgress(0); txtInfo.setText("播放已经停止"); } catch (Exception e) { Log.i("LAT", "停止异常!"); } } }); btnLast = (Button) findViewById(R.id.btn_last); btnLast.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { musicService.last(); } catch (Exception e) { Log.i("LAT", "上一曲异常!"); } } }); btnNext = (Button) findViewById(R.id.btn_next); btnNext.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { musicService.next(); } catch (Exception e) { Log.i("LAT", "下一曲异常!"); } } }); seekBar = (SeekBar) findViewById(R.id.sb); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int i, boolean b) {//用于监听SeekBar进度值的改变 } @Override public void onStartTrackingTouch(SeekBar seekBar) {//用于监听SeekBar开始拖动 } @Override public void onStopTrackingTouch(SeekBar seekBar) {//用于监听SeekBar停止拖动 SeekBar停止拖动后的事件 int progress = seekBar.getProgress(); Log.i("TAG:", "" + progress + ""); int musicMax = musicService.player.getDuration(); //得到该首歌曲最长秒数 int seekBarMax = seekBar.getMax(); musicService.player .seekTo(musicMax * progress / seekBarMax);//跳到该曲该秒 autoChange = true; manulChange = false; } }); VideoThreed threed=new VideoThreed(); threed.start(); } //向列表添加MP3名字 private void setListViewAdapter() { String[] str = new String[musicService.musicList.size()]; int i = 0; for (String path : musicService.musicList) { File file = new File(path); str[i++] = file.getName(); } ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, str); listView = (ListView) findViewById(R.id.lv1); listView.setAdapter(adapter); } //设置当前播放的信息 private String setPlayInfo(int position, int max) { String info = "正在播放: " + musicService.songName + "\t\t"; int pMinutes = 0; while (position >= 60) { pMinutes++; position -= 60; } String now = (pMinutes < 10 ? "0" + pMinutes : pMinutes) + ":" + (position < 10 ? "0" + position : position); int mMinutes = 0; while (max >= 60) { mMinutes++; max -= 60; } String all = (mMinutes < 10 ? "0" + mMinutes : mMinutes) + ":" + (max < 10 ? "0" + max : max); return info + now + " / " + all; } }

    最后,还要在AndroidManifest.xml中添加像SD操作的权限!在此附上我的AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.jack.musicplayer"> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 向SD卡写入数据权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 在SD卡中创建与删除文件权限 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" tools:ignore="ProtectedPermissions" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

    希望以往能给你带来帮助,我知道刚开始写肯定写的不好,所以请多对见谅!

    最新回复(0)