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

Android之简单本地音乐播放器

2016-03-17 16:52 295 查看
平台:Android studio

APK:http://fir.im/apps/56ea5187e75e2d69af000042

本地的音乐播放器,主要功能就是可以播放音乐,能够读取本地的音乐,并显示出来,播放,暂停,上一首,下一首,进度条可以拖拽播放,添加了前台service,看一下实现





首先我是先做了一个大概的布局,样子先出来,需要其他的空间后期再添加,毕竟一开始不可能想的太详细,看一下主布局文件,就是一个listView 和几个ImageButton,又自己添加了一个title,有个可以进度条,需要可以拖动,使用了seekBar

<?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"
tools:context="com.cl.android.music.MusicActivity"
android:background="@color/background"
android:id="@+id/contentmusic"
>

<include layout="@layout/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:id="@+id/include" />
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/musicListView"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/include"
android:layout_above="@+id/seekBar" />
<SeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/seekBar"
android:layout_above="@+id/musicinfo"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<TextView
android:layout_width="match_parent"
android:layout_height="25dp"
android:gravity="center"

android:id="@+id/musicinfo"
android:layout_above="@+id/previous"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<ImageView
android:id="@+id/previous"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@drawable/previous"
android:onClick="previous"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<ImageView
android:id="@+id/play_pause"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@drawable/play"
android:onClick="play_pause"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
<ImageView
android:id="@+id/next"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@drawable/next"
android:onClick="next"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
<!--<ImageButton
android:id="@+id/previous"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/previous"
android:onClick="previous"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<ImageButton
android:id="@+id/play_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/play"
android:onClick="play_pause"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
<ImageButton
android:id="@+id/next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/next"
android:onClick="next"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />-->

</RelativeLayout>
每个音乐文件显示时的样式
<?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">

<ImageView
android:layout_width="60dp"
android:layout_height="60dp"
android:id="@+id/video_imageView" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/video_title"
android:text="@string/music_title"
android:layout_alignTop="@+id/video_size"
android:layout_alignLeft="@+id/video_singer"
android:layout_alignStart="@+id/video_singer" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/video_singer"
android:text="@string/music_singer"
android:layout_marginLeft="20dp"
android:layout_marginStart="20dp"
android:layout_alignBaseline="@+id/video_duration"
android:layout_alignBottom="@+id/video_duration"
android:layout_toRightOf="@+id/video_imageView"
android:layout_toEndOf="@+id/video_imageView" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/video_size"
android:text="@string/music_size"
android:layout_above="@+id/video_duration"
android:layout_alignLeft="@+id/video_duration"
android:layout_alignStart="@+id/video_duration" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/video_duration"
android:text="@string/music_duration"
android:layout_marginRight="75dp"
android:layout_marginEnd="75dp"
android:layout_alignBottom="@+id/video_imageView"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginBottom="15dp" />

</RelativeLayout>
以及最上面的那个title

<?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:background="@color/toolbarbackground"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30sp"
android:text="@string/app_name"
android:id="@+id/textView"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
<ImageView
android:id="@+id/click_share"
android:onClick="clickShare"
android:layout_width="20dp"
android:layout_height="wrap_content"
android:src="@drawable/share"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="29dp"
android:layout_marginEnd="29dp" />
</RelativeLayout>
大概的布局出来后,就可以在填写逻辑代码了,思路肯定是先找到本地的音乐文件再显示出来了,本来思路是想扫描SD卡获取本地文件,找出MP3文件,这样也是可以的,但是Android系统会在SD卡有更新的时候自动将SD卡文件分类(视频/音频/图片...),并存入SQLite数据库,就保存在媒体存储器里面(com.android.providers.media),而我们要做的只是像正常读取数据库一样去读数据库的信息就好了,所以直接上代码,代码里都有注释

当前需要先加权限,src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />


/*加载媒体库里的音频*/
public ArrayList<MusicMedia> scanAllAudioFiles(){
//生成动态数组,并且转载数据
ArrayList<MusicMedia> mylist = new ArrayList<MusicMedia>();

/*查询媒体数据库
参数分别为(路径,要查询的列名,条件语句,条件参数,排序)
视频:MediaStore.Video.Media.EXTERNAL_CONTENT_URI
图片;MediaStore.Images.Media.EXTERNAL_CONTENT_URI

*/
Cursor cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
//遍历媒体数据库
if(cursor.moveToFirst()){
while (!cursor.isAfterLast()) {
//歌曲编号
int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID));
//歌曲标题
String tilte = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
//歌曲的专辑名:MediaStore.Audio.Media.ALBUM
String album = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));
int albumId = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
//歌曲的歌手名: MediaStore.Audio.Media.ARTIST
String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
//歌曲文件的路径 :MediaStore.Audio.Media.DATA
String url = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));
//歌曲的总播放时长 :MediaStore.Audio.Media.DURATION
int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION));
//歌曲文件的大小 :MediaStore.Audio.Media.SIZE
Long size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE));

if (size >1024*800){//大于800K
MusicMedia musicMedia = new MusicMedia();
musicMedia.setId(id);
musicMedia.setArtist(artist);
musicMedia.setSize(size);
musicMedia.setTitle(tilte);
musicMedia.setTime(duration);
musicMedia.setUrl(url);
musicMedia.setAlbum(album);
musicMedia.setAlbumId(albumId);

mylist.add(musicMedia);

}
cursor.moveToNext();
}
}
return mylist;
}
获取到之后我需要显示在list里,需要ArryList<Map<Object,object>>格式的数据,我上面是存储的一个对象,因为后面我需要单个音乐文件的信息,如果不需要单个文件信息可以在这里直接返回这种格式的数据,listView显示数据

musicList  = scanAllAudioFiles();
//这里其实可以直接在扫描时返回 ArrayList<Map<String, Object>>()
listems = new ArrayList<Map<String, Object>>();
for (Iterator iterator = musicList.iterator(); iterator.hasNext();) {
Map<String, Object> map = new HashMap<String, Object>();
MusicMedia mp3Info = (MusicMedia) iterator.next();
//            map.put("id",mp3Info.getId());
map.put("title", mp3Info.getTitle());
map.put("artist", mp3Info.getArtist());
map.put("album", mp3Info.getAlbum());
//            map.put("albumid", mp3Info.getAlbumId());
map.put("duration", mp3Info.getTime());
map.put("size", mp3Info.getSize());
map.put("url", mp3Info.getUrl());

map.put("bitmap", R.drawable.musicfile);

listems.add(map);

}

/*SimpleAdapter的参数说明
* 第一个参数 表示访问整个android应用程序接口,基本上所有的组件都需要
* 第二个参数表示生成一个Map(String ,Object)列表选项
* 第三个参数表示界面布局的id  表示该文件作为列表项的组件
* 第四个参数表示该Map对象的哪些key对应value来生成列表项
* 第五个参数表示来填充的组件 Map对象key对应的资源一依次填充组件 顺序有对应关系
* 注意的是map对象可以key可以找不到 但组件的必须要有资源填充  因为 找不到key也会返回null 其实就相当于给了一个null资源
* 下面的程序中如果 new String[] { "name", "head", "desc","name" } new int[] {R.id.name,R.id.head,R.id.desc,R.id.head}
* 这个head的组件会被name资源覆盖
* */
SimpleAdapter mSimpleAdapter = new SimpleAdapter(
this,
listems,
R.layout.music_item,
new String[] {"bitmap","title","artist", "size","duration"},
new int[] {R.id.video_imageView,R.id.video_title,R.id.video_singer,R.id.video_size,R.id.video_duration}
);
//listview里加载数据
musicListView.setAdapter(mSimpleAdapter);
现在音乐列表显示在listView里了,可以点击,可以上下滑动,但是怎么实现点击播放呢,使用MediaPler,这是Android系统自带的播放器,这里给出其常用的方法的中文解释,其实大可以直接看英文API嘛

MediaPlayer 常用方法介绍
方法:create(Context context, Uri uri)
解释:静态方法,通过Uri创建一个多媒体播放器。
方法:create(Context context, int resid)
解释:静态方法,通过资源ID创建一个多媒体播放器
方法:create(Context context, Uri uri, SurfaceHolder holder)
解释:静态方法,通过Uri和指定 SurfaceHolder 【抽象类】 创建一个多媒体播放器
方法: getCurrentPosition()
解释:返回 Int, 得到当前播放位置
方法: getDuration()
解释:返回 Int,得到文件的时间
方法:getVideoHeight()
解释:返回 Int ,得到视频的高度
方法:getVideoWidth()
解释:返回 Int,得到视频的宽度
方法:isLooping()
解释:返回 boolean ,是否循环播放
方法:isPlaying()
解释:返回 boolean,是否正在播放
方法:pause()
解释:无返回值 ,暂停
方法:prepare()
解释:无返回值,准备同步
方法:prepareAsync()
解释:无返回值,准备异步
方法:release()
解释:无返回值,释放 MediaPlayer  对象
方法:reset()
解释:无返回值,重置 MediaPlayer  对象
方法:seekTo(int msec)
解释:无返回值,指定播放的位置(以毫秒为单位的时间)
方法:setAudioStreamType(int streamtype)
解释:无返回值,指定流媒体的类型
方法:setDataSource(String path)
解释:无返回值,设置多媒体数据来源【根据 路径】
方法:setDataSource(FileDescriptor fd, long offset, long length)
解释:无返回值,设置多媒体数据来源【根据 FileDescriptor】
方法:setDataSource(FileDescriptor fd)
解释:无返回值,设置多媒体数据来源【根据 FileDescriptor】
方法:setDataSource(Context context, Uri uri)
解释:无返回值,设置多媒体数据来源【根据 Uri】
方法:setDisplay(SurfaceHolder sh)
解释:无返回值,设置用 SurfaceHolder 来显示多媒体
方法:setLooping(boolean looping)
解释:无返回值,设置是否循环播放
事件:setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener)
解释:监听事件,网络流媒体的缓冲监听
事件:setOnCompletionListener(MediaPlayer.OnCompletionListener listener)
解释:监听事件,网络流媒体播放结束监听
事件:setOnErrorListener(MediaPlayer.OnErrorListener listener)
解释:监听事件,设置错误信息监听
事件:setOnVideoSizeChangedListener(MediaPlayer.OnVideoSizeChangedListener listener)
解释:监听事件,视频尺寸监听
方法:setScreenOnWhilePlaying(boolean screenOn)
解释:无返回值,设置是否使用 SurfaceHolder 显示
方法:setVolume(float leftVolume, float rightVolume)
解释:无返回值,设置音量
方法:start()
解释:无返回值,开始播放
方法:stop()
解释:无返回值,停止播放
也就是说,使用mediaplayer需要传一个地址过去,这些信息在刚读取音频的方法里都有,直接使用就好了,使用一个service播放,新建一个service,不考虑其他,启动一个隐式的service,需要做一些修改,传一个地址过去先播放起来

在listview上添加监听器,怎么知道点击的音乐的地址,哈哈,有个position,可以定位到musiclist里具体的对象

musicListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//点击播放音乐,不过需要判断一下当前是否有音乐在播放,需要关闭正在播放的
//position 可以获取到点击的是哪一个,去 musicList 里寻找播放
currentposition = position;
player(currentposition);
}
});


新建service,修改AndroidManifest.xml 文件
<service
android:name=".MusicPlayerService"
android:enabled="true"
android:exported="true">
<intent-filter >
<action android:name="player"></action>
</intent-filter>
</service>
启动service

intent.setAction("player");
intent.setPackage(getPackageName());
intent.putExtra("url", musicList.get(position).getUrl());
startService(intent);
再看看service端

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand......3");
// /storage/emulated/0/Music/Download/Selena Gomez - Revival/Hands to Myself.mp3
if(intent != null){

url = intent.getStringExtra("url");
mediaPlayer.setDataSource(url);
mediaPlayer.setLooping(true);//单曲循环
mediaPlayer.prepare();
mediaPlayer.start();
}
return super.onStartCommand(intent, flags, startId);
}
这样就可以直接播放啦,虽然现在只能播放,但是毕竟可以播放了,下面继续播放控制部分逻辑

进度条怎么显示当前的播放进度,我一开始的想法是service使用静态的变量,activity里也是使用静态的变量,然后开一个线程,1s更新一次状态,在service里直接修改seekbar,我就直接上代码了
public class MusicPlayerService extends Service implements Runnable {
private static final String TAG = "MusicPlayerService";
private static final int NOTIFICATION_ID = 1; // 如果id设置为0,会导致不能设置为前台service
public static MediaPlayer mediaPlayer = null;
private String url = null;
private String MSG = null;
private static int curposition;//第几首音乐
private musicBinder musicbinder = null;
private int currentPosition = 0;// 设置默认进度条当前位置
public MusicPlayerService() {
Log.i(TAG,"MusicPlayerService......1");
musicbinder = new musicBinder();
}

//通过bind 返回一个IBinder对象,然后改对象调用里面的方法实现参数的传递
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG,"onBind......");
return null;
}

@Override
public void onCreate() {
Log.i(TAG, "onCreate......2");
super.onCreate();
if (mediaPlayer == null) {
/* mediaPlayer.reset();
mediaPlayer.release();
mediaPlayer = null;*/
mediaPlayer = new MediaPlayer();
}
// 监听播放是否完成
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
//我目前也不知道该干嘛

}
});

}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand......3");
// /storage/emulated/0/Music/Download/Selena Gomez - Revival/Hands to Myself.mp3
if(intent != null){
MSG = intent.getStringExtra("MSG");
if(MSG.equals("0")){
url = intent.getStringExtra("url");
Log.i(TAG, url + "......." + Thread.currentThread().getName());
palyer();
}else if(MSG.equals("1")){
mediaPlayer.pause();
}else if(MSG.equals("2")){
mediaPlayer.start();
}

}

return super.onStartCommand(intent, flags, startId);
}

private void palyer() {
Log.i(TAG,"palyer......");
//如果正在播放,先停止再播放新的
/* if(mediaPlayer.isPlaying()){
Log.i(TAG,"palyer......running....");
// 暂停
mediaPlayer.pause();
mediaPlayer.reset();
}*/
//还有就是用户在暂停是点击其他的音乐,所以不管当前状态,都重置一下
//下面这段代码可以实现简单的音乐播放
try {
//            Log.i(TAG,"palyer......new....");
mediaPlayer.reset();

mediaPlayer.setDataSource(url);
mediaPlayer.setLooping(true);
//            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.prepare();
mediaPlayer.start();
// 设置进度条最大值
MusicActivity.audioSeekBar.setMax(mediaPlayer.getDuration());
//开启新线程
new Thread(this).start();
} catch (IOException e) {
e.printStackTrace();
}
}
// 刷新进度条 ,时间

@Override
public void run() {

Log.i(TAG,Thread.currentThread().getName()+"......run...");

int total = mediaPlayer.getDuration();// 总时长
while (mediaPlayer != null && currentPosition < total) {
try {
Thread.sleep(1000);
if (mediaPlayer != null) {
currentPosition = mediaPlayer.getCurrentPosition();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
MusicActivity.audioSeekBar.setProgress(CurrentPosition);

}

}

@Override
public void onDestroy() {
Log.i(TAG,"onDestroy......");
super.onDestroy();
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
//关闭线程
Thread.currentThread().interrupt();
stopForeground(true);
}
public String toTime(int time){
time /= 1000;
int minute = time / 60;
int hour = minute / 60;
int second = time % 60;
minute %= 60;
return String.format("%02d:%02d", minute, second);
}
}
这样子搞不是不可以,但是官方提供的onbind方法没有使用,所以想使用一下,调用onbind放回一个service对象,在activity的连接部分获取到这个返回值,然后在activity里直接使用该对象的方法获取想要的数据,再添加上前台service,完成后的代码

package com.cl.android.music;

import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;

import java.io.IOException;

public class MusicPlayerService extends Service {//implements Runnable {
private static final String TAG = "MusicPlayerService";
private static final int NOTIFICATION_ID = 1; // 如果id设置为0,会导致不能设置为前台service
public static MediaPlayer mediaPlayer = null;
private String url = null;
private String MSG = null;
private static int curposition;//第几首音乐
private musicBinder musicbinder = null;
private int currentPosition = 0;// 设置默认进度条当前位置
public MusicPlayerService() {
Log.i(TAG,"MusicPlayerService......1");
musicbinder = new musicBinder();
}

//通过bind 返回一个IBinder对象,然后改对象调用里面的方法实现参数的传递
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG,"onBind......");
return musicbinder;
}
/**
* 自定义的 Binder对象
*/
public class musicBinder extends Binder {
public MusicPlayerService getPlayInfo(){
return MusicPlayerService.this;
}
}
//得到当前播放位置
public  int getCurrentPosition(){

if(mediaPlayer != null){
int total = mediaPlayer.getDuration();// 总时长
if( currentPosition < total){
currentPosition = mediaPlayer.getCurrentPosition();
}
}
return currentPosition;
}
//得到当前播放位置
public  int getDuration(){
return mediaPlayer.getDuration();// 总时长
}

//得到 mediaPlayer
public MediaPlayer getMediaPlayer(){
//        if(mediaPlayer != null){
//            return mediaPlayer;
//        }
return mediaPlayer;
}
//得到 当前播放第几个音乐
public int getCurposition(){
return curposition;
}
@Override
public void onCreate() {
Log.i(TAG, "onCreate......2");
super.onCreate();
if (mediaPlayer == null) {
/* mediaPlayer.reset();
mediaPlayer.release();
mediaPlayer = null;*/
mediaPlayer = new MediaPlayer();
}
// 监听播放是否完成
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
//我目前也不知道该干嘛

}
});

}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand......3");
// /storage/emulated/0/Music/Download/Selena Gomez - Revival/Hands to Myself.mp3
if(intent != null){
MSG = intent.getStringExtra("MSG");
if(MSG.equals("0")){
url = intent.getStringExtra("url");
curposition = intent.getIntExtra("curposition",0);
Log.i(TAG, url + "......." + Thread.currentThread().getName());
palyer();
}else if(MSG.equals("1")){
mediaPlayer.pause();
}else if(MSG.equals("2")){
mediaPlayer.start();
}

String name = "Current: "+ url.substring(url.lastIndexOf("/") + 1 , url.lastIndexOf("."));
Log.i(TAG,name);
//        //开启前台service
Notification notification = null;
if (Build.VERSION.SDK_INT < 16) {
notification = new Notification.Builder(this)
.setContentTitle("Enter the MusicPlayer").setContentText(name)
.setSmallIcon(R.drawable.musicfile).getNotification();
} else {
Notification.Builder builder = new Notification.Builder(this);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, MusicActivity.class), 0);
builder.setContentIntent(contentIntent);
builder.setSmallIcon(R.drawable.musicfile);
//        builder.setTicker("Foreground Service Start");
builder.setContentTitle("Enter the MusicPlayer");
builder.setContentText(name);
notification = builder.build();
}

startForeground(NOTIFICATION_ID, notification);
}

return super.onStartCommand(intent, flags, startId);
}

private void palyer() {
Log.i(TAG,"palyer......");
//如果正在播放,先停止再播放新的
/* if(mediaPlayer.isPlaying()){
Log.i(TAG,"palyer......running....");
// 暂停
mediaPlayer.pause();
mediaPlayer.reset();
}*/
//还有就是用户在暂停是点击其他的音乐,所以不管当前状态,都重置一下
//下面这段代码可以实现简单的音乐播放
try {
//            Log.i(TAG,"palyer......new....");
mediaPlayer.reset();

mediaPlayer.setDataSource(url);
mediaPlayer.setLooping(true);
//            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.prepare();
mediaPlayer.start();
// 设置进度条最大值
//            MusicActivity.audioSeekBar.setMax(mediaPlayer.getDuration());
//开启新线程
//            new Thread(this).start();
} catch (IOException e) {
e.printStackTrace();
}
}
// 刷新进度条 ,时间

/*
@Override
public void run() {

Log.i(TAG,Thread.currentThread().getName()+"......run...");

int total = mediaPlayer.getDuration();// 总时长
while (mediaPlayer != null && currentPosition < total) {
try {
Thread.sleep(1000);
if (mediaPlayer != null) {
currentPosition = mediaPlayer.getCurrentPosition();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
//            MusicActivity.audioSeekBar.setProgress(CurrentPosition);

}

}
*/

@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG,"onUnbind......");
return super.onUnbind(intent);

}

@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
Log.i(TAG, "onRebind......");
}

@Override
public void onDestroy() {
Log.i(TAG,"onDestroy......");
super.onDestroy();
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
//关闭线程
Thread.currentThread().interrupt();
stopForeground(true);
}
public String toTime(int time){
time /= 1000;
int minute = time / 60;
int hour = minute / 60;
int second = time % 60;
minute %= 60;
return String.format("%02d:%02d", minute, second);
}
}
activity那端,使用handler + runnable实现主线程的界面刷新,全部功能完成后的代码

package com.cl.android.music;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.database.Cursor;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
import android.view.View.OnClickListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

public class MusicActivity extends AppCompatActivity {

private ListView musicListView = null;
private ImageView imageView = null;
private ArrayList<Map<String, Object>> listems = null;//需要显示在listview里的信息
private ArrayList<MusicMedia> musicList = null; //音乐信息列表
// private ImageButton btn_previous = null,btn_play_pause = null,btn_next = null;
private ImageView btn_play_pause = null;
public static SeekBar audioSeekBar = null;//定义进度条
public static TextView textView = null;
private Intent intent = null;
private int currentposition = -1;//当前播放列表里哪首音乐
private boolean isplay = false;//音乐是否在播放
private MusicPlayerService musicPlayerService = null;
private MediaPlayer mediaPlayer = null;
private Handler handler = null;//处理界面更新,seekbar ,textview
private boolean isservicerunning = false;//退出应用再进入时(点击app图标或者在通知栏点击service)使用,判断服务是否在启动
private SingleMusicInfo singleMusicInfo = null;//音乐的详细信息
private boolean isExit = false;//返回键
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_music);
Log.i("MusicPlayerService", "MusicActivity...onCreate........." + Thread.currentThread().hashCode());
// init();

}

private void init() {

intent = new Intent();
intent.setAction("player");
intent.setPackage(getPackageName());

handler = new Handler();
imageView = (ImageView)findViewById(R.id.click_share);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_TEXT,"我的博客地址:http://blog.csdn.net/i_do_can");
shareIntent.setType("text/plain");
//设置分享列表
startActivity(Intent.createChooser(shareIntent,"分享到"));
}
});

textView = (TextView)findViewById(R.id.musicinfo);

musicListView = (ListView)findViewById(R.id.musicListView);

// btn_previous = (ImageButton)findViewById(R.id.previous);
//播放暂停时要切换图标
// btn_play_pause = (ImageButton)findViewById(R.id.play_pause);
btn_play_pause = (ImageView)findViewById(R.id.play_pause);
// btn_next = (ImageButton)findViewById(R.id.next);

musicListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //点击播放音乐,不过需要判断一下当前是否有音乐在播放,需要关闭正在播放的 //position 可以获取到点击的是哪一个,去 musicList 里寻找播放 currentposition = position; player(currentposition); } });

musicList = scanAllAudioFiles(); //这里其实可以直接在扫描时返回 ArrayList<Map<String, Object>>() listems = new ArrayList<Map<String, Object>>(); for (Iterator iterator = musicList.iterator(); iterator.hasNext();) { Map<String, Object> map = new HashMap<String, Object>(); MusicMedia mp3Info = (MusicMedia) iterator.next(); // map.put("id",mp3Info.getId()); map.put("title", mp3Info.getTitle()); map.put("artist", mp3Info.getArtist()); map.put("album", mp3Info.getAlbum()); // map.put("albumid", mp3Info.getAlbumId()); map.put("duration", mp3Info.getTime()); map.put("size", mp3Info.getSize()); map.put("url", mp3Info.getUrl()); map.put("bitmap", R.drawable.musicfile); listems.add(map); } /*SimpleAdapter的参数说明 * 第一个参数 表示访问整个android应用程序接口,基本上所有的组件都需要 * 第二个参数表示生成一个Map(String ,Object)列表选项 * 第三个参数表示界面布局的id 表示该文件作为列表项的组件 * 第四个参数表示该Map对象的哪些key对应value来生成列表项 * 第五个参数表示来填充的组件 Map对象key对应的资源一依次填充组件 顺序有对应关系 * 注意的是map对象可以key可以找不到 但组件的必须要有资源填充 因为 找不到key也会返回null 其实就相当于给了一个null资源 * 下面的程序中如果 new String[] { "name", "head", "desc","name" } new int[] {R.id.name,R.id.head,R.id.desc,R.id.head} * 这个head的组件会被name资源覆盖 * */ SimpleAdapter mSimpleAdapter = new SimpleAdapter( this, listems, R.layout.music_item, new String[] {"bitmap","title","artist", "size","duration"}, new int[] {R.id.video_imageView,R.id.video_title,R.id.video_singer,R.id.video_size,R.id.video_duration} ); //listview里加载数据 musicListView.setAdapter(mSimpleAdapter);

//进度条
audioSeekBar = (SeekBar) findViewById(R.id.seekBar);

//退出后再次进去程序时,进度条保持持续更新
if(MusicPlayerService.mediaPlayer!=null){
reinit();//更新页面布局以及变量相关
}

//播放进度监 ,使用静态变量时别忘了Service里面还有个进度条刷新
audioSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (currentposition == -1) {
Log.i("MusicPlayerService", "MusicActivity...showInfo(请选择要播放的音乐);.........");
//还没有选择要播放的音乐
showInfo("请选择要播放的音乐");
} else {
//假设改变源于用户拖动
if (fromUser) {
//这里有个问题,如果播放时用户拖进度条还好说,但是如果是暂停时,拖完会自动播放,所以还需要把图标设置一下
btn_play_pause.setBackgroundResource(R.drawable.pause);
MusicPlayerService.mediaPlayer.seekTo(progress);// 当进度条的值改变时,音乐播放器从新的位置开始播放
}

}
}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {
if (mediaPlayer != null) {
mediaPlayer.pause();
}

// MusicPlayerService.mediaPlayer.pause(); // 开始拖动进度条时,音乐暂停播放
}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (mediaPlayer != null) {
mediaPlayer.start();
}
// MusicPlayerService.mediaPlayer.start(); // 停止拖动进度条时,音乐开始播放
}
});

//textView 点击弹出音乐的详细信息
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Log.i("MusicPlayerService", "MusicActivity...textView.setOnClickListener;.........");
if (textView.getText().length() > 0) {
singleMusicInfo = new SingleMusicInfo(MusicActivity.this,listems.get(currentposition));
//显示窗口
singleMusicInfo.showAtLocation(MusicActivity.this.findViewById(R.id.contentmusic), Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0); //设置layout在PopupWindow中显示的位置
}
}

});
Log.i("MusicPlayerService", "MusicActivity...init done;.........");

}

private void reinit() {
//设置进度条最大值
// audioSeekBar.setMax(MusicPlayerService.mediaPlayer.getDuration());
// audioSeekBar.setProgress(MusicPlayerService.mediaPlayer.getCurrentPosition());
// currentposition = MusicPlayerService.getCurposition();
Log.i("MusicPlayerService","reinit.........");
isservicerunning = true;
//如果是正在播放
if(MusicPlayerService.mediaPlayer.isPlaying()){
isplay = true;
btn_play_pause.setBackgroundResource(R.drawable.pause);
}
//重新绑定service
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}

/*加载媒体库里的音频*/ public ArrayList<MusicMedia> scanAllAudioFiles(){ //生成动态数组,并且转载数据 ArrayList<MusicMedia> mylist = new ArrayList<MusicMedia>(); /*查询媒体数据库 参数分别为(路径,要查询的列名,条件语句,条件参数,排序) 视频:MediaStore.Video.Media.EXTERNAL_CONTENT_URI 图片;MediaStore.Images.Media.EXTERNAL_CONTENT_URI */ Cursor cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER); //遍历媒体数据库 if(cursor.moveToFirst()){ while (!cursor.isAfterLast()) { //歌曲编号 int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)); //歌曲标题 String tilte = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)); //歌曲的专辑名:MediaStore.Audio.Media.ALBUM String album = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM)); int albumId = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID)); //歌曲的歌手名: MediaStore.Audio.Media.ARTIST String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)); //歌曲文件的路径 :MediaStore.Audio.Media.DATA String url = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA)); //歌曲的总播放时长 :MediaStore.Audio.Media.DURATION int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)); //歌曲文件的大小 :MediaStore.Audio.Media.SIZE Long size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE)); if (size >1024*800){//大于800K MusicMedia musicMedia = new MusicMedia(); musicMedia.setId(id); musicMedia.setArtist(artist); musicMedia.setSize(size); musicMedia.setTitle(tilte); musicMedia.setTime(duration); musicMedia.setUrl(url); musicMedia.setAlbum(album); musicMedia.setAlbumId(albumId); mylist.add(musicMedia); } cursor.moveToNext(); } } return mylist; }

public void previous(View view) {
previousMusic();
}

public void play_pause(View view) {
Log.i("MusicPlayerService", "MusicActivity...play_pause........." +isplay);
//当前是pause的图标,(使用图标来判断是否播放,就不需要再新定义变量为状态了,表示没能找到得到当前背景的图片的)实际上播放着的,暂停
// if(btn_play_pause.getBackground().getCurrent().equals(R.drawable.play)){
if(isservicerunning){//服务启动着,这里点击播放暂停按钮时只需要当前音乐暂停或者播放就好
if (isplay) {
pause();
} else {
//暂停--->继续播放
player("2");
}
}else {
if (isplay) {
pause();
} else {
Log.i("MusicPlayerService", "MusicActivity...notplay.........");
//当前是play的图标,是 暂停 着的
//初始化时,没有点击列表,直接点击了播放按钮
if (currentposition == -1) {
showInfo("请选择要播放的音乐");
} else {
//暂停--->继续播放
player("2");
}
}
}

}

public void next(View view) {
nextMusic();
}

private void player() {
player(currentposition);
}

private void player(int position){

textView.setText(musicList.get(position).getTitle()+" playing...");

intent.putExtra("curposition", position);//把位置传回去,方便再启动时调用
intent.putExtra("url", musicList.get(position).getUrl());
intent.putExtra("MSG","0");
isplay = true;
//播放时就改变btn_play_pause图标,下面这个过期了
// btn_play_pause.setBackgroundDrawable(getResources().getDrawable(R.drawable.pause));
btn_play_pause.setBackgroundResource(R.drawable.pause);

startService(intent);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
Log.i("MusicPlayerService","MusicActivity...bindService.......");

}
private ServiceConnection conn = new ServiceConnection() {
/** 获取服务对象时的操作 */
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
musicPlayerService = ((MusicPlayerService.musicBinder)service).getPlayInfo();
mediaPlayer = musicPlayerService.getMediaPlayer();
Log.i("MusicPlayerService", "MusicActivity...onServiceConnected.......");
currentposition = musicPlayerService.getCurposition();
//设置进度条最大值
audioSeekBar.setMax(mediaPlayer.getDuration());
//这里开了一个线程处理进度条,这个方式官方貌似不推荐,说违背什么单线程什么鬼
// new Thread(seekBarThread).start();
//使用runnable + handler
handler.post(seekBarHandler);
}

/** 无法获取到服务对象时的操作 */
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
musicPlayerService = null;
}

};

//1s更新一次进度条
Runnable seekBarThread = new Runnable() {
@Override
public void run() {
while (musicPlayerService != null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Log.i("MusicPlayerService", "seekBarThread run.......");

audioSeekBar.setProgress(musicPlayerService.getCurrentPosition());

}
}
};
Runnable seekBarHandler = new Runnable() {
@Override
public void run() {

Log.i("MusicPlayerService", "MusicActivity...seekBarHandler run......."+Thread.currentThread().hashCode()+" "+handler.hashCode());
audioSeekBar.setProgress(musicPlayerService.getCurrentPosition());
textView.setText( "(Click Me) "+ musicList.get(currentposition).getTitle() +" " +
musicPlayerService.toTime(musicPlayerService.getCurrentPosition()) +
" / " + musicPlayerService.toTime(musicPlayerService.getDuration() ));
handler.postDelayed(seekBarHandler, 1000);

}
};

private void player(String info){

intent.putExtra("MSG",info);
isplay = true;
btn_play_pause.setBackgroundResource(R.drawable.pause);
startService(intent);

}
/*
* MSG :
* 0 未播放--->播放
* 1 播放--->暂停
* 2 暂停--->继续播放
*
* */
private void pause() {
intent.putExtra("MSG","1");
isplay = false;
btn_play_pause.setBackgroundResource(R.drawable.play);
startService(intent);
}
private void previousMusic() {
if(currentposition > 0){
currentposition -= 1;
player();
}else{
showInfo("已经是第一首音乐了");
}
}

private void nextMusic() {
if(currentposition < musicList.size()-2){
currentposition += 1;
player();
}else{
showInfo("已经是最后一首音乐了");
}
}

private void showInfo(String info) {
Toast.makeText(this,info,Toast.LENGTH_SHORT).show();
}

@Override
protected void onResume() {
super.onResume();
Log.i("MusicPlayerService", "MusicActivity...onResume........." + Thread.currentThread().hashCode());
init();
}

@Override
protected void onPause() {
super.onPause();
Log.i("MusicPlayerService", "MusicActivity...onPause........." + Thread.currentThread().hashCode());
//绑定服务了
if(musicPlayerService != null){
unbindService(conn);
}
handler.removeCallbacks(seekBarHandler);

}

@Override
protected void onDestroy() {
super.onDestroy();
// unbindService(conn);
Log.i("MusicPlayerService", "MusicActivity...onDestroy........." + Thread.currentThread().hashCode());
}
private void exit(String info) {
if(!isExit) {
isExit = true;
Toast.makeText(this, info, Toast.LENGTH_SHORT).show();
new Timer().schedule(new TimerTask() {
@Override
public void run() {
isExit = false;
}
}, 2000);
} else {
finish();
}
}
//按两次返回键退出
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK
&& event.getRepeatCount() == 0) {
//音乐服务启动了,隐藏至通知栏
if(musicPlayerService != null){
exit("再按一次隐藏至通知栏");
}else{
exit("再按一次退出程序");
}

}
return false;
}

}


有个很奇怪的地方,完全不符合生命周期,按说应用不在界面上显示,再启动时,应该调用onRestart->onStart...
但是我测试时时直接OnCreate-onStart...好吧,很无奈,直接kill掉了,可能是我手机的问题,为了防止真的是手机的问题,我把初始化代码写在了onResume里
当前播放时间显示的部分,我添加了一个弹窗,点击可以显示当前播放歌曲的详细信息,弹窗继承PopupWindow,布局文件就一个TableLayout,就俩列,设置第二列内容自动换行

<?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"
>
<TableLayout
android:id="@+id/tablelayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:shrinkColumns="1"></TableLayout>

</RelativeLayout>


弹窗点击非弹窗部分退出

package com.cl.android.music;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.view.ViewGroup.LayoutParams;
import android.widget.SimpleAdapter;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

/**
* Created by chenling on 2016/3/17.
*/
public class SingleMusicInfo extends PopupWindow {
private View view;
private TableLayout tableLayout;

public SingleMusicInfo(Context context,Map<String, Object> map) {
super(context);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.singlemusicinfo, null);

tableLayout = (TableLayout) view.findViewById(R.id.tablelayout);

map.remove("bitmap");//移除图片那个键值对
for(String keys : map.keySet()){
TableRow tableRow = new TableRow(context);
TextView key = new TextView(context);
TextView value = new TextView(context);
Log.i("MusicPlayerService", "SingleMusicInfo..........." + tableRow.hashCode());
key.setText("   " + keys + "    ");
value.setText(map.get(keys).toString());
tableRow.addView(key);
tableRow.addView(value);
tableLayout.addView(tableRow);
}

//设置SingleMusicInfo的View
this.setContentView(view);
//设置弹出窗体的宽
this.setWidth(LayoutParams.MATCH_PARENT);
//设置弹出窗体的高
this.setHeight(LayoutParams.WRAP_CONTENT);
//设置S弹出窗体可点击
this.setFocusable(true);
ColorDrawable dw = new ColorDrawable(Color.rgb(255,228,181));
//设置弹出窗体的背景
this.setBackgroundDrawable(dw);
//view添加OnTouchListener监听判断获取触屏位置如果在选择框外面则销毁弹出框
view.setOnTouchListener(new OnTouchListener() {

public boolean onTouch(View v, MotionEvent event) {

int height = view.findViewById(R.id.tablelayout).getTop();
int y=(int) event.getY();
if(event.getAction()==MotionEvent.ACTION_UP){
if(y<height){
dismiss();
}
}
return true;
}
});

}
}
Ok ,原谅我代码逻辑没有讲详细,代码都有注释,注释的代码部分可以直接忽略,还有分享功能,大家果断分享一下吧,谢谢

附件:源码下载:http://download.csdn.net/detail/i_do_can/9464424

- - - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - 更新2016-04-09- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
新增列表上拉下滑时顶部和底部菜单栏消失
新增播放模式:单曲循环 随机播放 顺序播放

新增摇一摇根据当前播放模式切换歌曲
更新之后的apk:http://fir.im/5u9p



第一点,列表上拉下滑时顶部和底部菜单栏消失,关键是要监听onTouchLIstener,关键代码如下

private float mLastY = -1;// 标记上下滑动时上次滑动位置,滑动隐藏上下标题栏
private RelativeLayout musictop,musicbotom;
musictop = (RelativeLayout)findViewById(R.id.music_top);
musicbotom = (RelativeLayout)findViewById(R.id.music_bottom);
//上下滚动时
musicListView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {

if (mLastY == -1) {
mLastY = event.getRawY();
}
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
//判断上滑还是下滑
if (event.getRawY() > mLastY) {
//下滑显示bottom,隐藏top
musictop.setVisibility(View.GONE);
musicbotom.setVisibility(View.VISIBLE);
} else if (event.getRawY() < mLastY) {
//上滑,显示top,隐藏bottom
musictop.setVisibility(View.VISIBLE);
//                            musicbotom.setVisibility(View.INVISIBLE);
musicbotom.setVisibility(View.GONE);

} else {
// deltaY = 0.0 时
musictop.setVisibility(View.VISIBLE);
musicbotom.setVisibility(View.VISIBLE);
mLastY = event.getRawY();
return false;//返回false即可响应click事件
}
mLastY = event.getRawY();
break;
default:
// reset
mLastY = -1;
musictop.setVisibility(View.VISIBLE);
musicbotom.setVisibility(View.VISIBLE);
break;
}
return false;
}
});
接着看看播放模式的改变,这个简单,用户看见的只是简单的图片的切换,我这里是使用了SharedPreferences 保存相关的信息,方便用户再次启动时更改显示图片,主要是保存当前的播放模式,主要代码如下:
public static SharedPreferences sharedPreferences;
public static SharedPreferences.Editor editor;//保存播放模式
private ImageView playMode ,playaccelerometer;
private int[] modepic = {R.drawable.ic_shuffle_black_24dp,R.drawable.ic_repeat_black_24dp,R.drawable.ic_repeat_one_black_24dp};
//默认随机播放
playMode = (ImageView)findViewById(R.id.play_mode);
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
editor = sharedPreferences.edit();
int playmode = sharedPreferences.getInt("play_mode", -1);
if(playmode == -1){//没有设置模式,默认随机
editor.putInt("play_mode",0).commit();
}else{
changeMode(playmode);
}
//修改播放模式  单曲循环 随机播放 顺序播放
int clicktimes = 0;
public void changeMode(View view) {
switch (clicktimes){
case 0://随机 --> 顺序
clicktimes++;
changeMode(clicktimes);
break;
case 1://顺序 --> 单曲
clicktimes++;
changeMode(clicktimes);
break;
case 2://单曲 --> 随机
clicktimes = 0;
changeMode(clicktimes);
break;
default:
break;
}

}
private void changeMode(int playmode) {
editor.putInt("play_mode",playmode).commit();
playMode.setBackgroundResource(modepic[playmode]);
}
其实这里最关键的不是上面的代码,而是播放器里的控制,模式更改后,当前音乐播放完成后要根据用户选择的模式来播放新的音乐,需要设置监听 mediaPlayer.setOnCompletionListener,然后根据SharedPreferences
里的模式来播放音乐,是顺序播放还是随机播放,我的services里没有音乐列表,所以需要从activity里先获取音乐列表,以及当前播放的哪首音乐,

单曲循环时播放器的url不需要更改,顺序播放时取得下一首位置:curposition = (++curposition) % musiclist.size()
随机播放时取得下一首位置:curposition = (new Random()).nextInt(musiclist.size());
关键代码如下

// 监听播放是否完成
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
//我目前也不知道该干嘛,下一首嘛
playnew();
}
});
private void playnew() {
switch (MusicActivity.sharedPreferences.getInt("play_mode",-1)){
case 0://随机
curposition = (new Random()).nextInt(musiclist.size());
url =  musiclist.get(curposition ).getUrl();
palyer();
break;
case 1://顺序
curposition = (++curposition) % musiclist.size();
url =  musiclist.get(curposition ).getUrl();
palyer();
break;
case 2://单曲
url =  musiclist.get(curposition ).getUrl();
palyer();
break;
default:
break;
}

}
以上基本就可以实现自己控制播放模式了,

接着实现摇一摇切歌:这个功能我直接写在service里了,切歌时震动一下,需要在 AndroidManifest.xml 里添加权限

<!--震动-->
<uses-permission android:name="android.permission.VIBRATE"/>
摇一摇需要用到加速传感器,传感器Android底层都给我们实现好了,直接实现接口implements SensorEventListener

主要代码:
private SensorManager sensorManager = null;//传感器
private Vibrator vibrator = null;//震动
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
//取得震动服务的句柄
vibrator = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);
//加速度传感器(accelerometer)、陀螺仪(gyroscope)、环境光照传感器(light)、磁力传感器(magnetic field)、方向传感器(orientation)、压力传感器(pressure)、距离传感器(proximity)和温度传感器(temperature)。
// http://www.open-open.com/lib/view/open1378259498734.html sensorManager.registerListener(this,
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_NORMAL);
@Override
public void onSensorChanged(SensorEvent event) {
if(MusicActivity.sharedPreferences.getInt("play_accelerometer",0) == 0){
// 传感器报告新的值
int sensorType = event.sensor.getType();
//values[0]:X轴,values[1]:Y轴,values[2]:Z轴
float[] values = event.values;
if (sensorType == Sensor.TYPE_ACCELEROMETER)
{
if ((Math.abs(values[0]) > 17 || Math.abs(values[1]) > 17 || Math
.abs(values[2]) > 17))
{
Log.i("slack", "sensor x values[0] = " + values[0]);
Log.i("slack", "sensor y values[1] = " + values[1]);
Log.i("slack", "sensor z values[2] = " + values[2]);
playnew();
//摇动手机后,再伴随震动提示~~
vibrator.vibrate(500);
}

}
}
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
//传感器精度的改变
}
基本就完成了

附件:module源码下载:http://download.csdn.net/detail/i_do_can/9485662
git地址:https://github.com/CL-window/Music
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: