利用Android的媒体库,遍历SD卡中的音乐文件,并用Service制作简单的音乐播放
2016-02-18 17:06
786 查看
在前文中利用递归的方法遍历SD卡中的音乐文件/article/10639791.html,现在利用Android中的媒体库,遍历SD卡中的音乐文件。效果如下:
效果图中的音乐列表图片:音乐文件如.mp3 一般都包含有该音乐的基本信息,如图片,歌曲名,艺术家等。注意:没有歌词。下面这个类就可以得到这些信息。
MusicListActivity.java :
该类中用到的 MusicBean.java ,MusicBean 就是数据的传递,关于 Parcelable 在前面的博客中都有介绍:
MainActivity.java :
使用 Service 时,利用接口实现 MainActivity 和 Service 之间的数据传递,Service 利用接口在 MainActivty 中更新图标状态,更新音乐播放进度,事件的响应等。
MusicService.java :
所需的布局文件:
1. MusicListActvity 中所有到的布局文件 :
activity_music_list_layout_sec.xml :
其中的 player_bg_bar.xml :
music_list_item.xml :
2. MainActivity 中的布局文件 :
activity_main.xml :
其中 SeekBar 当中用到的 seekbar_progressdrawable_selector.xml :
效果图中的音乐列表图片:音乐文件如.mp3 一般都包含有该音乐的基本信息,如图片,歌曲名,艺术家等。注意:没有歌词。下面这个类就可以得到这些信息。
MusicListActivity.java :
package com.crazy.crazymusicplayer; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.provider.MediaStore; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import com.crazy.crazymusicplayer.Utils.MusicBean; import java.io.File; import java.util.ArrayList; public class MusicListActivity extends Activity implements AdapterView.OnItemClickListener{ private ListView mListView; private Handler mHandler = new Handler(); private ArrayList<MusicBean> mMediaLists = new ArrayList<>(); private MusicListAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_music_list_layout_sec); mListView = (ListView)findViewById(R.id.music_list_view_sec); mListView.setOnItemClickListener(this); adapter = new MusicListAdapter(this); mListView.setAdapter(adapter); asyncQueryMedia(); } public void asyncQueryMedia() { new Thread(new Runnable() { @Override public void run() { mMediaLists.clear(); queryMusic(Environment.getExternalStorageDirectory() + File.separator); mHandler.post(new Runnable() { @Override public void run() { adapter.setListData(mMediaLists); } }); } }).start(); } /** * 获取目录下的歌曲 * * @param dirName */ public void queryMusic(String dirName) { Cursor cursor = getContentResolver().query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, MediaStore.Audio.Media.DATA + " like ?", new String[]{dirName + "%"}, MediaStore.Audio.Media.DEFAULT_SORT_ORDER); if (cursor == null) return; // id title singer data time image MusicBean music; for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { // 如果不是音乐 String isMusic = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.IS_MUSIC)); if (isMusic != null && isMusic.equals("")) continue; String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)); String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)); if (isRepeat(title, artist)) continue; music = new MusicBean(); music.setId(cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID))); music.setTitle(title); music.setArtist(artist); music.setMusicPath(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA))); music.setLength(cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION))); music.setImage(getAlbumImage(cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID)))); mMediaLists.add(music); } cursor.close(); } /** * 根据音乐名称和艺术家来判断是否重复包含了 * * @param title * @param artist * @return */ private boolean isRepeat(String title, String artist) { for (MusicBean music : mMediaLists) { if (title.equals(music.getTitle()) && artist.equals(music.getArtist())) { return true; } } return false; } /** * 根据歌曲id获取图片 * * @param albumId * @return */ private String getAlbumImage(int albumId) { String result = ""; Cursor cursor = null; try { cursor = getContentResolver().query( Uri.parse("content://media/external/audio/albums/" + albumId), new String[]{"album_art"}, null, null, null); for (cursor.moveToFirst(); !cursor.isAfterLast(); ) { result = cursor.getString(0); break; } } catch (Exception e) { } finally { if (null != cursor) { cursor.close(); } } return null == result ? null : result; } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { MusicListAdapter adapter = (MusicListAdapter) parent.getAdapter(); adapter.setPlayingPosition(position); Intent intent = new Intent(this,MainActivity.class); intent.putParcelableArrayListExtra("MUSIC_LIST", mMediaLists); intent.putExtra("CURRENT_POSITION", position); startActivity(intent); } private class MusicListAdapter extends BaseAdapter{ private LayoutInflater inflater; private ArrayList<MusicBean> list = new ArrayList<>(); private int mPlayingPosition; public MusicListAdapter(Context context) { inflater = LayoutInflater.from(context); } public void setPlayingPosition(int position) { mPlayingPosition = position; notifyDataSetChanged(); } public void setListData(ArrayList<MusicBean> list) { this.list = list; notifyDataSetChanged(); } @Override public int getCount() { return list.size(); } @Override public Object getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder= null; if (convertView == null) { convertView = inflater.inflate(R.layout.music_list_item, parent, false); viewHolder = new ViewHolder(); viewHolder.icon = (ImageView)convertView.findViewById(R.id.music_list_icon); viewHolder.title = (TextView)convertView.findViewById(R.id.tv_music_list_title); viewHolder.artist = (TextView)convertView.findViewById(R.id.tv_music_list_artist); viewHolder.mark = convertView.findViewById(R.id.music_list_selected); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder)convertView.getTag(); } if (mPlayingPosition == position) viewHolder.mark.setVisibility(View.VISIBLE); else viewHolder.mark.setVisibility(View.INVISIBLE); MusicBean music = (MusicBean) getItem(position); Bitmap icon = BitmapFactory.decodeFile(music.getImage()); viewHolder.icon.setImageBitmap(icon == null ? BitmapFactory.decodeResource( getResources(), R.drawable.skin_online_default_bg) : icon); viewHolder.title.setText(music.getTitle()); viewHolder.artist.setText(music.getArtist()); return convertView; } class ViewHolder { ImageView icon; TextView title, artist; View mark; } } }
该类中用到的 MusicBean.java ,MusicBean 就是数据的传递,关于 Parcelable 在前面的博客中都有介绍:
package com.crazy.crazymusicplayer.Utils; import android.os.Parcel; import android.os.Parcelable; public class MusicBean implements Parcelable{ private String musicName; private String musicPath; private String image; // icon private String artist; // 艺术家 private int length; // 长度 private int id; // 音乐id private String title; // 音乐标题 public String getMusicName() { return musicName; } public void setMusicName(String musicName) { this.musicName = musicName; } public String getImage() { return image; } public void setImage(String image) { this.image = image; } public String getArtist() { return artist; } public void setArtist(String artist) { this.artist = artist; } public int getLength() { return length; } public void setLength(int length) { this.length = length; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getMusicPath() { return musicPath; } public void setMusicPath(String musicPath) { this.musicPath = musicPath; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(musicName); dest.writeString(musicPath); dest.writeString(image); dest.writeString(artist); dest.writeInt(length); dest.writeInt(id); dest.writeString(title); } /** * 必须用 public static final 修饰符 * 对象必须用 CREATOR */ public static final Creator<MusicBean> CREATOR = new Creator<MusicBean>() { @Override public MusicBean createFromParcel(Parcel source) { MusicBean music = new MusicBean(); music.setMusicName(source.readString()); music.setMusicPath(source.readString()); music.setImage(source.readString()); music.setArtist(source.readString()); music.setLength(source.readInt()); music.setId(source.readInt()); music.setTitle(source.readString()); return music; } @Override public MusicBean[] newArray(int size) { return new MusicBean[size]; } }; }
MainActivity.java :
package com.crazy.crazymusicplayer; import android.app.Service; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.ImageButton; import android.widget.SeekBar; import android.widget.TextView; import com.crazy.crazymusicplayer.Utils.MusicBean; import java.lang.ref.WeakReference; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; /** * 由 MusicListActivity 启动 MainActivity */ public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private ImageButton bt_play, bt_pre, bt_next; private SeekBar seekBar; private MyHandler mHandler = new MyHandler(this); private SimpleDateFormat format = new SimpleDateFormat("mm:ss"); private TextView currentTimeTxt, totalTimeTxt; private MusicService.CallBack callBack; private boolean mFlag = true; private ArrayList<MusicBean> musicBeanList = new ArrayList<>(); private int mProgress; private static class MyHandler extends Handler { // 弱引用 private WeakReference<MainActivity> reference; public MyHandler(MainActivity activity) { reference = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { MainActivity activity = reference.get(); if (activity != null) { int currentTime = activity.callBack.callCurrentTime(); int totalTime = activity.callBack.callTotalDate(); activity.seekBar.setMax(totalTime); activity.seekBar.setProgress(currentTime); String current = activity.format .format(new Date(currentTime)); String total = activity.format.format(new Date(totalTime)); activity.currentTimeTxt.setText(current); activity.totalTimeTxt.setText(total); } } } private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { callBack = (MusicService.MyBinder)service; } @Override public void onServiceDisconnected(ComponentName name) { callBack = null; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); getMusInfoAndStService(); seekTime(); forSeekBar(); } private void initData(){ seekBar = (SeekBar)findViewById(R.id.seek_bar); bt_play = (ImageButton)findViewById(R.id.bt_play); bt_pre = (ImageButton)findViewById(R.id.bt_pre); bt_next = (ImageButton)findViewById(R.id.bt_next); currentTimeTxt = (TextView)findViewById(R.id.current_time_txt); totalTimeTxt = (TextView)findViewById(R.id.total_time_txt); bt_play.setOnClickListener(this); bt_pre.setOnClickListener(this); bt_next.setOnClickListener(this); } private void getMusInfoAndStService(){ /** 接收音乐列表资源 */ musicBeanList = getIntent().getParcelableArrayListExtra("MUSIC_LIST"); int currentPosition = getIntent().getIntExtra("CURRENT_POSITION", 0); /** 构造启动音乐播放服务的Intent,设置音乐资源 */ Intent intent = new Intent(this, MusicService.class); intent.putParcelableArrayListExtra("MUSIC_LIST", musicBeanList); intent.putExtra("CURRENT_POSITION", currentPosition); startService(intent); bindService(intent, conn, Service.BIND_AUTO_CREATE); } @Override protected void onResume() { super.onResume(); } @Override protected void onPause() { super.onPause(); } // 广播可以用来通知更新音乐文件等,此处可无 private class MyMainActivityReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { } } private void seekTime(){ new Thread(new Runnable() { @Override public void run() { while (mFlag) { if (callBack != null) { mHandler.sendMessage(Message.obtain()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }).start(); } private void forSeekBar(){ seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (callBack != null) mProgress = progress; } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { if (callBack != null) { callBack.iSeekTo(mProgress); } } }); } @Override public void onClick(View v) { switch (v.getId()) { // 播放或者暂停 case R.id.bt_play: playerMusicByIBinder(); break; case R.id.bt_pre: callBack.isPlayPre(); break; case R.id.bt_next: callBack.isPlayNext(); break; } } /** * 播放音乐通过Binder接口实现 */ public void playerMusicByIBinder() { boolean playerState = callBack.isPlayerMusic(); if (playerState) { bt_play.setImageResource(R.drawable.btn_playback_pause); } else { bt_play.setImageResource(R.drawable.btn_playback_play); } } @Override protected void onDestroy() { super.onDestroy(); if (conn != null || callBack != null) { unbindService(conn); callBack = null; } Intent intent = new Intent(this, MusicService.class); stopService(intent); mFlag = false; } }
使用 Service 时,利用接口实现 MainActivity 和 Service 之间的数据传递,Service 利用接口在 MainActivty 中更新图标状态,更新音乐播放进度,事件的响应等。
MusicService.java :
package com.crazy.crazymusicplayer; import android.app.Service; import android.content.Intent; import android.media.MediaPlayer; import android.os.Binder; import android.os.IBinder; import com.crazy.crazymusicplayer.Utils.MusicBean; import java.io.IOException; import java.util.ArrayList; public class MusicService extends Service { private MediaPlayer mPlayer; private ArrayList<MusicBean> musicPathLists; private int currentPos; public interface CallBack { boolean isPlayerMusic(); int callTotalDate(); int callCurrentTime(); void iSeekTo(int m_second); void isPlayPre(); void isPlayNext(); boolean isPlayering(); } public class MyBinder extends Binder implements CallBack { @Override public boolean isPlayerMusic() { return playerMusic(); } @Override public int callTotalDate() { if (mPlayer != null) { return mPlayer.getDuration(); } else { return 0; } } @Override public int callCurrentTime() { if (mPlayer != null) { return mPlayer.getCurrentPosition(); } else { return 0; } } @Override public void iSeekTo(int m_second) { if (mPlayer != null) { mPlayer.seekTo(m_second); } } @Override public void isPlayPre() { if (--currentPos < 0) { currentPos = 0; } initMusic(); playerMusic(); } @Override public void isPlayNext() { if (++currentPos > musicPathLists.size() - 1) { currentPos = musicPathLists.size() - 1; } initMusic(); playerMusic(); } @Override public boolean isPlayering() { if(mPlayer.isPlaying()){ return true; }else{ return false; } } } @Override public void onCreate() { super.onCreate(); mPlayer = new MediaPlayer(); } private void initMusic() { // 根路径 // String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmd.mp3"; mPlayer.reset(); try { mPlayer.setDataSource(musicPathLists.get(currentPos).getMusicPath()); mPlayer.prepare(); mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { currentPos++; if (currentPos >= musicPathLists.size()) { currentPos = 0; } // mp.start(); initMusic(); playerMusic(); } }); } catch (IOException e) { e.printStackTrace(); } } public boolean playerMusic() { if (mPlayer.isPlaying()) { mPlayer.pause(); return false; } else { mPlayer.start(); return true; } } @Override public int onStartCommand(Intent intent, int flags, int startId) { musicPathLists = intent.getParcelableArrayListExtra("MUSIC_LIST"); currentPos = intent.getIntExtra("CURRENT_POSITION", -1); initMusic(); playerMusic(); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent intent) { return new MyBinder(); } @Override public void onDestroy() { super.onDestroy(); if (mPlayer != null) { if (mPlayer.isPlaying()) { mPlayer.stop(); } mPlayer.release(); } } }
所需的布局文件:
1. MusicListActvity 中所有到的布局文件 :
activity_music_list_layout_sec.xml :
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="@drawable/player_bg_bar" > <ListView android:id="@+id/music_list_view_sec" android:layout_width="match_parent" android:layout_height="match_parent" > </ListView > </LinearLayout>
其中的 player_bg_bar.xml :
<?xml version="1.0" encoding="UTF-8"?> <bitmap android:src="@drawable/defend_open_background" android:tileMode="repeat" xmlns:android="http://schemas.android.com/apk/res/android" />
music_list_item.xml :
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="10dp"> <View android:id="@+id/music_list_selected" android:background="@android:color/holo_blue_dark" android:layout_width="2dp" android:layout_height="50dp" /> <ImageView android:id="@+id/music_list_icon" android:scaleType="centerCrop" android:layout_marginLeft="5dp" android:layout_toRightOf="@id/music_list_selected" android:src="@drawable/skin_online_default_bg" android:layout_width="50dp" android:layout_height="50dp" /> <TextView android:id="@+id/tv_music_list_title" android:text="音乐标题" android:textColor="@android:color/white" android:textSize="16sp" android:layout_marginLeft="5dp" android:layout_marginTop="3dp" android:singleLine="true" android:layout_toRightOf="@id/music_list_icon" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_music_list_artist" android:layout_marginLeft="5dp" android:layout_marginTop="3dp" android:maxLines="2" android:textSize="12sp" android:textColor="@android:color/white" android:text="艺术家" android:layout_toRightOf="@id/music_list_icon" android:layout_below="@id/tv_music_list_title" android:layout_width="match_parent" android:layout_height="wrap_content" /> </RelativeLayout>
2. MainActivity 中的布局文件 :
activity_main.xml :
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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:background="@drawable/bindbackground" tools:context="com.crazy.crazymusicplayer.MainActivity"> <LinearLayout android:id="@+id/play_pre_next" android:orientation="horizontal" android:gravity="center" android:layout_alignParentBottom="true" android:layout_marginBottom="10dp" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageButton android:id="@+id/bt_pre" android:background="@drawable/btn_playback_pre" android:layout_width="30dp" android:layout_height="30dp" /> <ImageButton android:id="@+id/bt_play" android:layout_marginLeft="20dp" android:background="@drawable/btn_playback_play" android:layout_width="30dp" android:layout_height="30dp" /> <ImageButton android:id="@+id/bt_next" android:layout_marginLeft="20dp" android:background="@drawable/btn_playback_next" android:layout_width="30dp" android:layout_height="30dp" /> </LinearLayout> <RelativeLayout android:id="@+id/song_lyrics_progress" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_above="@id/play_pre_next" android:layout_marginTop="20dp"> <TextView android:id="@+id/current_time_txt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_alignParentLeft="true" android:text="00:00" /> <TextView android:id="@+id/total_time_txt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="10dp" android:text="00:00" /> <SeekBar android:id="@+id/seek_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_toRightOf="@id/current_time_txt" android:layout_toLeftOf="@id/total_time_txt" android:layout_centerVertical="true" android:progressDrawable="@drawable/seekbar_progressdrawable_selector" android:thumb="@drawable/player_progress_thumb"/> </RelativeLayout> <TextView android:id="@+id/song_lyrics" android:text="歌词" android:textSize="22sp" android:layout_marginTop="5dp" android:layout_centerHorizontal="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/song_lyrics_detail" android:text="歌词的详细内容" android:maxLines="8" android:layout_marginTop="40dp" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:layout_above="@id/song_lyrics_progress" android:layout_below="@id/song_lyrics" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
其中 SeekBar 当中用到的 seekbar_progressdrawable_selector.xml :
<?xml version="1.0" encoding="UTF-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@android:id/background" android:drawable="@drawable/progress_bar_n"> </item> <item android:id="@android:id/progress"> <clip> <nine-patch android:src="@drawable/progress_bar_p" /> </clip> </item> </layer-list>本例中的 progress_bar_n 和 progress_bar_p 为 .9.png图片,放在 drawable-xxhdpi 当中。上面的 player_progress_thumb.png 图片放在 drawable-xhdpi 当中,这样由于分辨率的不同才能有效果图中的效果(在本例中是这样,或者从新制作图片)。
相关文章推荐
- android 软键盘完全解析 InputMethodManager
- 关于Android中android:marginBottom不起作用以及android:signleLine对文字的影响
- 在Fragment如何使用共享元素转场动画
- Android中处理崩溃异常——补充内容
- android 开发 百度地图 最新指定区域截图
- Android之Monkey全参数(包含隐藏参数)
- Android中通过进程注入技术修改系统返回的Mac地址
- 国内优秀Android学习资源
- Android APK反编译得到Java源代码和资源文件
- android悬浮窗口的实现
- android: qq 5.0 demo学习笔记(主 粒子爆炸效果+ViewDragHelper)
- android到底可以加载多大的图不报OOM错误
- Android基础篇(三)——Android中的Activity简单介绍
- Android常用布局样式
- LeakCanary:跟OOM说再见
- AndroidStudio使用笔记
- android中activity的四种启动模式
- Android 中解决ScrollView嵌套ListView或者GridView时的冲突问题
- 关于android apk系统签名
- 利用ffmepg和HLS实现ios和android浏览器在线视频播放