android音乐播放器(Service+ContentProvider+Broadcast+Activity四大组件完成)

时间:2023-11-13 13:54:32

1、获取音乐

  1-1:获取手机中的音乐(用ContentProvider内容提供者来完成):

 package com.firefly.util;

 import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import android.content.Context;
import android.database.Cursor;
import android.provider.MediaStore;
import android.util.Log; public class MediaUtil {
/**
* 用于从数据库中查询歌曲的信息,保存在List当中
*
* @return
*/
public static List<Mp3Info> getMp3Infos(Context context) {
Cursor cursor = context.getContentResolver().query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,
MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
List<Mp3Info> mp3Infos = new ArrayList<Mp3Info>(); for (int i = 0; i < cursor.getCount(); i++) {
cursor.moveToNext();
Mp3Info mp3Info = new Mp3Info();
long id = cursor.getLong(cursor
.getColumnIndex(MediaStore.Audio.Media._ID)); // 音乐id
String title = cursor.getString((cursor
.getColumnIndex(MediaStore.Audio.Media.TITLE))); // 音乐标题
String artist = cursor.getString(cursor
.getColumnIndex(MediaStore.Audio.Media.ARTIST)); // 艺术家
long duration = cursor.getLong(cursor
.getColumnIndex(MediaStore.Audio.Media.DURATION)); // 时长
long size = cursor.getLong(cursor
.getColumnIndex(MediaStore.Audio.Media.SIZE)); // 文件大小
String url = cursor.getString(cursor
.getColumnIndex(MediaStore.Audio.Media.DATA)); // 文件路径
int isMusic = cursor.getInt(cursor
.getColumnIndex(MediaStore.Audio.Media.IS_MUSIC)); // 是否为音乐
if (isMusic != 0) { // 只把音乐添加到集合当中 mp3Info.setId(id);
mp3Info.setTitle(title);
mp3Info.setArtist(artist);
mp3Info.setDuration(duration);
mp3Info.setSize(size);
mp3Info.setUrl(url); mp3Infos.add(mp3Info);
}
}
return mp3Infos;
} /**
* 格式化时间,将毫秒转换为分:秒格式
*
* @param time
* @return
*/
public static String formatTime(long time) {
String min = time / (1000 * 60) + "";
String sec = time % (1000 * 60) + "";
if (min.length() < 2) {
min = "0" + time / (1000 * 60) + "";
} else {
min = time / (1000 * 60) + "";
}
if (sec.length() == 4) {
sec = "0" + (time % (1000 * 60)) + "";
} else if (sec.length() == 3) {
sec = "00" + (time % (1000 * 60)) + "";
} else if (sec.length() == 2) {
sec = "000" + (time % (1000 * 60)) + "";
} else if (sec.length() == 1) {
sec = "0000" + (time % (1000 * 60)) + "";
}
return min + ":" + sec.trim().substring(0, 2);
}
}

MediaUtil

  1-2:写实体类来存放获取的音乐数据

 package com.firefly.util;

 public class Mp3Info {
public long id; // 音乐id
public String title;// 音乐标题
public String artist;// 艺术家
public long duration; // 时长
public long size;// 文件大小
public String url;// 文件路径
public int isMusic;// 是否为音乐 public long getId() {
return id;
} public void setId(long id) {
this.id = id;
} public String getTitle() {
return title;
} public void setTitle(String title) {
this.title = title;
} public String getArtist() {
return artist;
} public void setArtist(String artist) {
this.artist = artist;
} public long getDuration() {
return duration;
} public void setDuration(long duration) {
this.duration = duration;
} public long getSize() {
return size;
} public void setSize(long size) {
this.size = size;
} public String getUrl() {
return url;
} public void setUrl(String url) {
this.url = url;
} public int getIsMusic() {
return isMusic;
} public void setIsMusic(int isMusic) {
this.isMusic = isMusic;
} }

Mp3Info

  1-3:写适配器来将音乐数据初始化

 package com.firefly.util;

 import java.util.List;

 import com.firefly.beautifulmusic.R;

 import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView; public class MusicAdapter extends BaseAdapter {
List<Mp3Info> datas;
LayoutInflater inflater;
int index = -1; // 把当前位置传过来
public void setIndex(int index) {
this.index = index;
} public MusicAdapter(Context context, List<Mp3Info> datas) {
this.inflater = LayoutInflater.from(context);
this.datas = datas;
} @Override
public int getCount() {
// TODO Auto-generated method stub
return datas.size();
} @Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return datas.get(position);
} @Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.item_music_list, null);
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.item_title);
holder.duration = (TextView) convertView
.findViewById(R.id.item_duration);
holder.artist = (TextView) convertView
.findViewById(R.id.item_artist);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Mp3Info music = datas.get(position);
holder.title.setTag(music);
holder.title.setText(music.getTitle());
holder.artist.setText(music.getArtist());
// 获取时间并改变它的格式
Long time = music.getDuration();
long min = (time / 1000) / 60;
long second = (time / 1000) % 60;
holder.duration.setText(min + ":" + second);
if (position == index) {
// 如果正在播放当前项,就改其颜色
holder.title.setTextColor(Color.parseColor("#E34C36"));
holder.duration.setTextColor(Color.parseColor("#E34C36"));
holder.artist.setTextColor(Color.parseColor("#E34C36"));
} else {
// 否则的话则回到原来的状态
holder.title.setTextColor(Color.BLACK);
holder.duration.setTextColor(Color.BLACK);
holder.artist.setTextColor(Color.BLACK);
}
return convertView;
} static class ViewHolder {
public TextView title;
public TextView duration;
public TextView artist;
} }

MusicAdapter

  1-4:(可不参考)用来适配的素材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="horizontal" > <LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical" > <TextView
android:id="@+id/item_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="歌名" /> <TextView
android:id="@+id/item_artist"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="歌手" />
</LinearLayout> <TextView
android:id="@+id/item_duration"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="4"
android:text="时长" /> </LinearLayout>

item_music_list

2、在主页面中进行初始化数据

  2-1:xml页面布局

 <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="${relativePackage}.${activityClass}" > <ListView
android:id="@+id/lv_musicList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" >
</ListView> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal" > <Button
android:id="@+id/btnUp"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@drawable/up"
android:onClick="playMusic" /> <Button
android:id="@+id/btnPlay"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:background="@drawable/pause"
android:onClick="playMusic" /> <Button
android:id="@+id/btnDown"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@drawable/down"
android:onClick="playMusic" />
</LinearLayout> <SeekBar
android:id="@+id/sb"
android:layout_width="match_parent"
android:layout_height="wrap_content" /> </LinearLayout>

activity_main

  2-2:在其相对应的Activiity文件中初始化数据

 package com.firefly.beautifulmusic;

 import java.io.File;
import java.util.List; import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.graphics.Color;
import android.os.Bundle;
import android.os.IBinder;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast; import com.firefly.service.MusicService;
import com.firefly.service.MusicService.MyBinder;
import com.firefly.util.MediaUtil;
import com.firefly.util.Mp3Info;
import com.firefly.util.MusicAdapter; public class MainActivity extends Activity {
ServiceConnection conn;
MusicService ms;
String music_name;
File f;
Boolean isPlay = false;
ListView lv;
MusicAdapter adapter;
List<Mp3Info> datas;
Button btn;
int current;
SeekBar sb; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化数据
init();
} private void init() {
btn = (Button) findViewById(R.id.btnPlay);
lv = (ListView) findViewById(R.id.lv_musicList);
//获取手机中 的音乐数据
datas = MediaUtil.getMp3Infos(MainActivity.this);
sb = (SeekBar) findViewById(R.id.sb);
adapter = new MusicAdapter(MainActivity.this, datas);
lv.setAdapter(adapter);
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
//点击哪一项就播放哪一项(前提条件是它是音乐文件)
current = position;
f = new File(datas.get(current).getUrl());
if (f.exists() && f.length() > 0) {
play();
} else {
Toast.makeText(getApplicationContext(), "该文件不是音乐", 0)
.show();
}
}
});
// 注册广播
broadcast();
// 服务
service();
} private void broadcast() {
// TODO Auto-generated method stub
MusicBroadcast mb = new MusicBroadcast();
IntentFilter ifilter = new IntentFilter();
ifilter.addAction("com.firefly.music");
ifilter.addAction("android.intent.action.PHONE_STATE");
registerReceiver(mb, ifilter);
Log.e("TAG", "动态注册成功");
} private void service() {
// TODO Auto-generated method stub
conn = new ServiceConnection() { @Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub } @Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
MyBinder binder = (MyBinder) service;
ms = binder.getService();
ms.setSeekBar(sb);
}
}; // 绑定服务
Intent i = new Intent(this, MusicService.class);
bindService(i, conn, BIND_AUTO_CREATE);
} public void playMusic(View v) {
switch (v.getId()) {
case R.id.btnPlay:
if (isPlay == false) {
play();
} else {
pause();
}
break;
// 上一首
case R.id.btnUp:
if (isPlay) {
if ((current - 1) >= 0) {
--current;
f = new File(datas.get(current).getUrl());
play();
} else {
Toast.makeText(getApplicationContext(), "已经是第一首了", 0).show();
}
}
break;
// 下一首
case R.id.btnDown:
if ((current + 1) < datas.size()) {
++current;
f = new File(datas.get(current).getUrl());
play();
} else {
Toast.makeText(getApplicationContext(), "已经是最后一首了", 0).show();
}
break; default:
break;
}
} //播放
public void play() {
//调用服务中的播放方法
ms.PlayMusic(f.getAbsolutePath());
btn.setBackgroundResource(R.drawable.play);
isPlay = true;
// 如果在播放中,就改变它的样式
adapter.setIndex(current);
adapter.notifyDataSetChanged();
} //暂停
public void pause() {
//调用服务中的暂停方法
ms.PauseMusic();
btn.setBackgroundResource(R.drawable.pause);
isPlay = false;
} // 新建广播
public class MusicBroadcast extends BroadcastReceiver { @Override
public void onReceive(Context context, Intent intent) {
// 接收广播
if (intent.getAction() == "com.firefly.music") {
if (intent.getStringExtra("end").equals("播放完毕")) {
if ((current + 1) < datas.size()) {
//如果播放完毕了,就播放下一首
f = new File(datas.get(++current).getUrl());
play();
} else {
Toast.makeText(getApplicationContext(), "已经是最后一首了", 0)
.show();
}
}
} else if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
//电话监听事件,如果要拨打电话就暂停音乐
String rs = getResultData();
Toast.makeText(context, "你在拨打" + rs + "的电话了,现在我已经暂停音乐了。", 0).show();
pause();
} else {
//来电也要暂停音乐
TelephonyManager telMan = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
// 判断电话的状态
switch (telMan.getCallState()) {
case TelephonyManager.CALL_STATE_RINGING:
Log.e("TAG", "响铃");
Toast.makeText(getApplicationContext(), "来电话了", 0).show();
pause();
break; case TelephonyManager.CALL_STATE_IDLE:
Log.e("TAG", "挂断");
Toast.makeText(getApplicationContext(), "您已经挂断,音乐将继续播放", 0).show();
play();
break; case TelephonyManager.CALL_STATE_OFFHOOK:
Log.e("TAG", "接听");
Toast.makeText(getApplicationContext(), "您正在接收电话,音乐已经暂停", 0).show();
pause();
break; default:
break;
}
}
} }
}

  2-3:写一个接口类,统一管理服务(也可不写)

 package com.firefly.service;

 public interface IMusic {
// 新建一个接口,能用服务来实现它,在这里可以统一管理
public void PlayMusic(String music_name); public void PauseMusic(); public void PreviousMusic(); public void NextMusic();
}

IMusic

  2-4:服务类

 package com.firefly.service;

 import android.app.Service;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.Toast; public class MusicService extends Service implements IMusic {
MediaPlayer music;
String music_name;
SeekBar sb;
Handler handler;
boolean flag = false;// 控制线程的播放 @Override
public void PlayMusic(String music_name) {
this.music_name = this.music_name;
if (music != null && music.isPlaying()) {
// 如果在播放的状态下切换到下一首,先把原来的停止、释放、置空,再新建
music.stop();
music.release();
music = null;
try {
music = new MediaPlayer();
music.setAudioStreamType(AudioManager.STREAM_MUSIC);
music.setDataSource(music_name);
music.prepare();
music.start();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else { if (music != null) {// 这个状态可能是暂停可能是停止
// 如果只是把它暂停,重新开始就可以了
music.start();
} else {
// 第一次播放这首音乐
try {
music = new MediaPlayer();
music.setAudioStreamType(AudioManager.STREAM_MUSIC);
music.setDataSource(music_name);
music.prepareAsync();
// 准备监听事件
music.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
music.start();
// 设置SeekBar
int max = music.getDuration();
int cur = music.getCurrentPosition();
Log.e("TAG", max + "==" + cur);
sb.setMax(max);
sb.setProgress(cur);
sb.setEnabled(true);
}
});
// 音乐播放完毕的监听器
music.setOnCompletionListener(new OnCompletionListener() { @Override
public void onCompletion(MediaPlayer mp) {
music.stop();
music.release();
music = null;
// 发送一个广播,已经播放完毕了
Intent i = new Intent();
i.setAction("com.firefly.music");
i.putExtra("end", "播放完毕");
sendBroadcast(i); }
});
} catch (Exception e) {
e.printStackTrace();
}
}
flag = true;
}
// 这个线程用来控制进度条的更新
Thread th = new Thread(new Runnable() { @Override
public void run() {
while (flag) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Message msg = new Message();
msg.what = 0;
handler.sendMessage(msg);
}
}
});
th.start(); } @Override
public void PauseMusic() {
// TODO Auto-generated method stub
if (music != null && music.isPlaying()) {
music.pause();
flag = false;
}
} @Override
public void PreviousMusic() {
// TODO Auto-generated method stub } @Override
public void NextMusic() {
// TODO Auto-generated method stub } public void setSeekBar(final SeekBar sb) {
this.sb = sb;
sb.setEnabled(false);// 没开始播放的时候进度条是不能拖动的
sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
if (music != null) {
music.seekTo(seekBar.getProgress());
}
} @Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub } @Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub }
}); handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
if (music != null) {
// 播放和SeekBar同步
sb.setProgress(music.getCurrentPosition());
}
break; default:
break;
}
} }; } @Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return new MyBinder();
} public class MyBinder extends Binder {
public MusicService getService() {
return MusicService.this;
}
} }

MusicService

3、清单文件

 <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.firefly.beautifulmusic"
android:versionCode="1"
android:versionName="1.0" > <uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />
<!-- 读写SD卡的权限、读取电话状态的权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity> <service android:name="com.firefly.service.MusicService" >
</service>
</application> </manifest>

Manifest

4、效果图

android音乐播放器(Service+ContentProvider+Broadcast+Activity四大组件完成)