android音乐播放器代码
01./*变量声明*/
02.private ImageButton playBtn = null;//播放、暂停
03.private ImageButton latestBtn = null;//上一首
04.private ImageButton nextButton = null;//下一首
05.private ImageButton forwardBtn = null;//快进
06.private ImageButton rewindBtn = null;//快退
07.private TextView playtime = null;//已播放时间
08.private TextView durationTime = null;//歌曲时间
09.private SeekBar seekbar = null;//歌曲进度
10.private Handler handler = null;//用于进度条
11.private Handler fHandler = null;//用于快进
12.private int currentPosition;//当前播放位置
13.
14./*获得列表传过来的数据*/
15.@Override
16.protected void onCreate(Bundle savedInstanceState) {
17. super.onCreate(savedInstanceState);
18. setContentView(https://www.360docs.net/doc/b017956180.html,yout.play);
19. Intent intent = this.getIntent();
20. Bundle bundle = intent.getExtras();
21. _ids = bundle.getIntArray("_ids"); //获得保存音乐文件_ID的数组
22. position = bundle.getInt("position"); //获得应该播放的音乐的号数,既播放第几首
23. //代码未完,见下面的代码
24.}
25.
26./*初始化控件*/
27.playtime = (TextView)findViewById(R.id.playtime); //显示已经播放的时间
28.durationTime = (TextView)findViewById(R.id.duration); //显示歌曲总时间
29.playBtn = (ImageButton)findViewById(R.id.playBtn); //开始播放、暂停播放按钮
https://www.360docs.net/doc/b017956180.html,testBtn = (ImageButton)findViewById(https://www.360docs.net/doc/b017956180.html,testBtn); //播放上一首按钮
31.nextButton = (ImageButton)findViewById(R.id.nextBtn); //播放下一首按钮
32.forwardBtn = (ImageButton)findViewById(R.id.forwardBtn); //快进按钮
33.rewindBtn = (ImageButton)findViewById(R.id.rewindBtn); //快退按钮
34.seekbar = (SeekBar)findViewById(R.id.seekbar); //播放进度条
35.
36./*定义各控件的回调函数*/
37.playBtn.setOnClickListener(new View.OnClickListener() { //点击“播放、暂停”按钮时回调
38. @Override
39. public void onClick(View v) {
40. if (mp.isPlaying()){ //如果正在播放则暂停
41. pause();
42. playBtn.setBackgroundResource(
43. R.drawable.play_selecor); //更改按键状态图标
44. } else{ //如果没有播放则恢复播放
45. play();
46. playBtn.setBackgroundResource(
47. R.drawable.pause_selecor); //更改按键状态图标
48.
49. }
50. }
51.});
52.
https://www.360docs.net/doc/b017956180.html,testBtn.setOnClickListener(new View.OnClickListener() {//点击“播放上一
首”按钮时回调
54. @Override
55. public void onClick(View v) {
56. int num = _ids.length; //获得音乐的数目
57. if(position==0){ //如果已经时第一首则播放最后一首
58. position=num-1;
59. }else{ //否则播放上一首
60. position-=1;
61. }
62. int pos = _ids[position]; //得到将要播放的音乐的_ID
63. setup(); //做播放前的准备工作
64. play(); //开始播放
65. }
66.});
67.
68.nextButton.setOnClickListener(new View.OnClickListener(){//点击“播放下一首”按钮时回调
69. @Override
70. public void onClick(View v) {
71. int num = _ids.length; //获得音乐的数目
72. if (position==num-1){ //如果已经是最后一首,则播放第一首
73. position=0;
74. }else{
75. position+=1; //否则播放下一首
76. }
77. int pos = _ids[position]; //得到将要播放的音乐的_ID
78. setup(); //做播放前的准备工作
79. play(); //开始播放
80. }
81.});
82.
83.forwardBtn.setOnTouchListener(new OnTouchListener() { //点击“快进”按钮时回调
84. @Override
85. public boolean onTouch(View v, MotionEvent event) {
86. switch (event.getAction()) {
87. case MotionEvent.ACTION_DOWN:
88. fHandler.post(forward); //此处使用handler对象更新进度条
89. mp.pause(); //点击快进按钮时,音乐暂停播放
90. break;
91.
92. case MotionEvent.ACTION_UP:
93. fHandler.removeCallbacks(forward);
94. mp.start(); //松开快进按钮时,音乐暂恢复播放
95. playBtn.setBackgroundResource(
96. R.drawable.pause_selecor);
97. break;
98. }
99. retu
rn false;
100. }
101.});
102.
103.rewindBtn.setOnTouchListener(new OnTouchListener() { //点击“快退”按钮时回调
104. @Override
105. public boolean onTouch(View v, MotionEvent event) {
106. switch (event.getAction()) {
107. case MotionEvent.ACTION_DOWN:
108. fHandler.post(rewind);
109. mp.pause(); //点击快退按钮时,音乐暂暂停播放
110. break;
111.
112. case MotionEvent.ACTION_UP:
113. fHandler.removeCallbacks(rewind);
114. mp.start(); //松开快退按钮时,音乐暂恢复播放
115. playBtn.setBackgroundResource(
116. R.drawable.pause_selecor);
117. break;
118. }
119. return false;
120. }
121.});
122.
123.seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
124. @Override
125. public void onStopTrackingTouch(SeekBar seekBar) {
126. mp.start(); //停止拖动进度条时,音乐开始播放
127. }
128. @Override
129. public void onStartTrackingTouch(SeekBar seekBar) {
130. mp.pause(); //开始拖动进度条时,音乐暂停播放
131. }
132.
133. @Override
134. public void onProgressChanged(SeekBar seekBar, int progress,
135. boolean fromUser) {
136. if(fromUser){
137. mp.seekTo(progress); //当进度条的值改变时,音乐播放器从新的位置开始播放
138. }
139. }
140.});
复制代码
最近做了一个android音乐播放器,个人感觉最难的就是“后台播放”以及有关“播放列表”的部分,但是总算是找到了实现的方式。不同的人实现的方式可能不一样,这里我就分享一下自己对“播放列表”这个模块的一些实现方法,“后台播放”会在下一篇博文中进行介绍,希望大家也能分享一下自己的一些思路。
android使用ContentProvider来支持不同应用程序的数据共享,为了方便其他应用程序对sdcard中的数据进行操作,sdcard也提供了ContentProvider接口,这里就以访问音频文件为例,视频以及图片的操作也类似,这里就不在赘述。
访问sdcard中的音频文件的URI为MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,为了使播放列表显示所以音乐文件的信息,这里需要查询sdcard里
的音频文件,并把查询到的信息保存在Cursor中,具体代码如下:
01.
Cursor c = this.getContentResolver().
02.
query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
03./*这个字符串数组表示要查询的列*/
复制代码
01.new String[]{MediaStore.Video.Media.TITLE, //音乐名
02.MediaStore.Audio.Media.DURATION, //音乐的总时间
03.MediaStore.Audio.Media.ARTIST, //艺术家
04.MediaStore.Audio.Media._ID, //id号
05.MediaStore.Audio.Media.DISPLAY_NAME, //音乐文件名
06.MediaStore.Audio.Media.DATA //音乐文件的路径
07.},
08.null, //查询条件,相当于sql中的where语句
09.null, //查询条件中使用到的数据
10.null); //查询结果的排序方式
复制代码
通过MediaStore.Audio.Media.XXX来访问音乐文件的一些信息,这里只列出了一部分,可以根据需要进行增添和删除。 至此,Cursor c就已经保存了sdcard内所以音频文件的信息,下面的操作就是围绕这个Cursor展开的。首先定义三个数组:
01.private int[] _ids; //存放音乐文件的id数组
02.private String[] _titles; //存放音乐文件的标题数组
03.private String[] _path; //存放音乐文件的路径
复制代码_ids保存了所有音乐文件的_ID,用来确定到底要播放哪一首歌曲,_titles存放音乐名,用来显示在播放界面,而_path存放音乐文件的路径(删除文件时会用到)。 接下来再定义一个变量,用来定位选择的是哪一首音乐:
private int pos;
接下来将音乐文件的信息存放在相应的数组中:
01.c.moveToFirst();
02._ids = new int[c.getCount()];
03._titles = new String[c.getCount()];
04._path = new String[c.getCount()];
05.for(int i=0;i
06. _ids[i] = c.getInt(3);
07. _titles[i] = c.getString(0);
08. _path[i] = c.getString(5).substring(4);
09. c.moveToNext();
10.}
复制代码
有人可能会问为什么获取路径的格式是_path=c.geString(5).substring(4)?因为MediaStore.Audio.Media.DATA
得到的内容格式为/mnt/sdcard/[子文件夹名/]音乐文件名,而我们想要得到的是/sdcard/[子文件夹名]音乐文件名,所以要做相应的裁剪操作。 接下来把Cursor中的信息显示到listview中:
01.MusicListAdapter adapter = new MusicListAdapter(this, c);
02.listview.setAdapter(adapter);
复制代码
MusicListAdapter是一个自定义的Adapter,继承自BaseAdapter,这里
只贴出代码,不做讲解。
01.package com.alex.video;
02.
03.import android.content.Context;
04.import android.database.Cursor;
05.import https://www.360docs.net/doc/b017956180.html,youtInflater;
06.import android.view.View;
07.import android.view.ViewGroup;
08.import android.widget.BaseAdapter;
09.import android.widget.ImageView;
10.import android.widget.TextView;
11.
12.public class MusicListAdapter extends BaseAdapter{
13. private Context myCon;
14. private Cursor myCur;
15. private int pos=-1;
16.
17. public MusicListAdapter(Context con,Cursor cur){
18. this.myCon = con;
19. this.myCur = cur;
20. }
21.
22. @Override
23. public int getCount() {
24.
25. return this.myCur.getCount();
26. }
27.
28. @Override
29. public Object getItem(int position) {
30.
31. return position;
32. }
33.
34. @Override
35. public long getItemId(int position) {
36.
37. return position;
38. }
39.
40. @Override
41. public View getView(int position, View convertView, ViewGroup parent) {
42. convertView = LayoutInflater.from(myCon).inflate(https://www.360docs.net/doc/b017956180.html,yout.musiclist,
43. null);
44. myCur.moveToPosition(position);
45. TextView videoTitle = (TextView)convertView.findViewById(R.id.musictitle);
46. if (myCur.getString(0).length()>24){
47. try {
48. String musicTitle = bSubstring(myCur.getString(0).trim(),24);
49. videoTitle.setText(musicTitle);
50. } catch (Exception e) {
51.
52. e.printStackTrace();
53. }
54. }else {
55. videoTitle.setText(myCur.getString(0).trim());
56. }
57. TextView videoArtist = (TextView)convertView.findViewById(R.id.musicartist);
58. if (myCur.getString(2).equals("
59. videoArtist.setText("未知艺术家");
60. }else{
61. videoArtist.setText(myCur.getString(2));
62. }
63. TextView videoTime = (TextView)convertView.findViewById(R.id.musictime);
64. videoTime.setText(toTime(myCur.getInt(1)));
65. ImageView videoItem = (ImageView)convertView.findViewById(R.id.musicitem);
66. videoItem.setImageResource(R.drawable.item);
67. return convertView;
68. }
69.
70. /*时间格式转换*/
71. public String toTime(int time) {
72.
73. time /= 1000;
74. int minute = time / 60;
75. int hour = minute / 60;
76. int second = time % 60;
77. minute %= 60;
78. return String.format("%02d:%02d", minute, second);
79. }
80.
81. /*字符串裁剪*/
82. public static String bSubstring(String s, int length) throws Exception
83. {
84.
85. byte[] bytes = s.getBytes("Unicode");
86. int n = 0; // 表示当前的字节数
87. int i = 2; // 要截取的字节数,从第3个字节开始
88. for (; i < bytes.length && n < length; i++)
89. {
90. // 奇数位置,如3、5、7等,为UCS2编码中两个字节的第二个字节
91. if (i % 2 == 1)
92. {
93. n++; // 在UCS2第二个字节时n加1
94. }
95. else
96. {
97. // 当UCS2编码的第一个字节不等于0时,该UCS2字符为汉字,一个汉字算两个字节
98. if (bytes[i] != 0)
99. {
100. n++;
101. }
102. }
103. }
104. // 如果i为奇数时,处理成偶数
105. if (i % 2 == 1)
106.
107. {
108. // 该UCS2字符是汉字时,去掉这个截一半的汉字
109. if (bytes[i - 1] != 0)
110. i = i - 1;
111. // 该UCS2字符是字母或数字,则保留该字符
112. else
113. i = i + 1;
114. }
115.
116. return new String(bytes, 0, i, "Unicode");
117. }
118.}
复制代码这样,音乐的信息就显示在了列表中了??????????????????????
下一节将讲解更新列表的操作。
在上一篇随笔中,我介绍了如何在程序中查询sdcard内的多媒体文件,并且显示到播放列表中,但是,如果在sdcard内删除、增加一些多媒体文件,如何让播放列表也更新呢,这里我分享一下自己在项目中的一些解决方法,希望对大家有所帮助。
首先,我简单介绍一下android是如何扫描sdcard内的多媒体信息的,详细请阅读stay的博文:
Android 多媒体【原创】
当android的系统启动的时候,系统会自动扫描sdcard内的多媒体文件,并把获得的信息保存在一个系
统数据库中,以后在其他程序中如果想要访问多媒体文件的信息,其实就是在这个数据库中进行的,而不是直接去sdcard中取,理解了这一点以后,问题也随着而来:如果我在开机状态下在sdcard内增加、删除一些多媒体文件,系统会不会自动扫描一次呢?答案是否定的,也就是说,当你改变sdcard内的多媒体文件时,保存多媒体信息的系统数据库文件是不会动态更新的。
那么如何让多媒体数据库中的数据更新呢?我们可以采用广播机制来实现:在应用程序中发送一个广播,让android系统扫描sdcard并更新多媒体数据库
01.private void scanSdCard(){
02. IntentFilter intentfilter = new IntentFilter( Intent.ACTION_MEDIA_SCANNER_STARTED);
03. intentfilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
04. intentfilter.addDataScheme("file");
05. scanSdReceiver = new ScanSdReceiver();
06. registerReceiver(scanSdReceiver, intentfilter);
07. sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
08. Uri.parse("file://" + Environment.getExternalStorageDirectory().getAbsolutePath()))); }
复制代码
其中ScanSdReceiver是一个自定义的广播接收器,继承自BroadCastReceiver,因为android系统开始扫描sdcard以及扫描完毕时都会发送一个系统广播来表示当前扫描的状态,这样我们就可以很方便通过判断当前的扫描状态加一些自己的逻辑操作,ScanSdReceiver的代码如下:
01.public class ScanSdReceiver extends BroadcastReceiver {
02.
03. private AlertDialog.Builder builder = null;
04. private AlertDialog ad = null;
05. private int count1;
06. private int count2;
07. private int count;
08. @Override
09. public void onReceive(Context context, Intent intent) {
10. String action = intent.getAction();
11. if (Intent.ACTION_MEDIA_SCANNER_STARTED.equals(action)){
12. Cursor c1 = context.getContentResolver()
13. .query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
14. new String[]{MediaStore.Audio.Media.TITLE,
15. MediaStore.Audio.Media.DURATION,
16. MediaStore.Audio.Media.ARTIST,
17. MediaStore.Audio.Media._ID,
18. MediaStore.Audio.Media.DISPLAY_NAME },
19. null, null, null);
20. count1 = c1.getCount();
21. System.out.println("count:"+count);
22. builder = new AlertDialog.Builder(c
ontext);
23. builder.setMessage("正在扫描存储卡...");
24. ad = builder.create();
25. ad.show();
26.
27. }else if(Intent.ACTION_MEDIA_SCANNER_FINISHED.equals(action)){
28. Cursor c2 = context.getContentResolver()
29. .query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
30. new String[]{MediaStore.Audio.Media.TITLE,
31. MediaStore.Audio.Media.DURATION,
32. MediaStore.Audio.Media.ARTIST,
33. MediaStore.Audio.Media._ID,
34. MediaStore.Audio.Media.DISPLAY_NAME },
35. null, null, null);
36. count2 = c2.getCount();
37. count = count2-count1;
38. ad.cancel();
39. if (count>=0){
40. Toast.makeText(context, "共增加" +
41. count + "首歌曲", Toast.LENGTH_LONG).show();
42. } else {
43. Toast.makeText(context, "共减少" +
44. count + "首歌曲", Toast.LENGTH_LONG).show();
45. }
46. }
47. }
48.}
这里我们定义了两个Cursor对象,分别用来存储扫描前后的多媒体信息,并给出相应的提示。
以前有很多朋友问过我,为什么扫描以后播放列表中的数据条数没有发生相应的改变。要实现播放列表在扫描后更新,必须重新读取多媒体信息到Cursor中,并且重新设置adapter,最后还要调用XXXAdapter.notifyDataSetChanged()来通知UI更新。(可以参考第一张的内容)
上面的操作都是手动在SDCARD中添加或着删除多媒体文件,下面介绍如何在列表中删除SDCARD中的多媒体文件。
在上一篇随笔中,我们使用了系统提供的ContentProvider来查询sdcard中的多媒体文件,我们同样可以使用这个ContentProvider来进行删除操作:
01.private void deleteMusic(int position){
02. this.getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
03. MediaStore.Audio.Media._ID + "=" + _ids[position],
04. null);
05.}
复制代码
其中“多媒体文件的ID”可以从_ids数组中取得(关于_ids数组请参考第一张的内容)。这样,我们是否就已经从SDCARD中删除了指定_ID的多媒体文件了呢? 其实当你打开FileExplorer时会发现,原来的文件并
没有被删除,— —!杯具,搞了半天文件还在,我第一次遇到这个问题的时候也是纠结了老半天。。。
为什么没有被删除,原因是上面的删除操作只是删除了系统多媒体数据库中的相应记录,而并没有删除SDCAED中的文件(注意:多媒体信息数据库和SDCARD中的多媒体文件并不会自动保持同步),这个时候如果再次扫描SDCARD,你会发现刚才从播放列表中删除的行会再次出现。
其实要想真正的从SDCARD中删除多媒体文件并不难,可能有朋友会想到这样的方法:遍历SDCARD中的多媒体文件,然后把想要删除的文件和其他文件逐一比较,找到文件路径,最后进行删除。这种方法是可以实现删除操作的,不过效率很低,如果SDCARD中的文件夹以及文件很多,也不知道要用掉多少时间。。
在上一张中我们从多媒体数据库中读出来一项很重要的信息:MediaStore.Audio.Media.DATA
并且取得里面的字串并存放在了_path数组中,最终的数据格式为:/SDCARD/[子文件夹名]文件名,又了这个路径,我们就可以很方便的从SDCARD中删除多媒体文件了
01.private void deleteMusicFile(int position){
02. File file = new File(_path[position]);
03. file.delete();
04.}
复制代码
同样,删除文件后要想播放列表同步更新,也必须执行刚才介绍的一系列操作。