您的位置:首页 > 移动开发 > Android开发

利用Android的媒体库,遍历SD卡中的音乐文件,并用Service制作简单的音乐播放

2016-02-18 17:06 786 查看
在前文中利用递归的方法遍历SD卡中的音乐文件/article/10639791.html,现在利用Android中的媒体库,遍历SD卡中的音乐文件。效果如下:



效果图中的音乐列表图片:音乐文件如.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 当中,这样由于分辨率的不同才能有效果图中的效果(在本例中是这样,或者从新制作图片)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: