Android之简单本地音乐播放器

平台:Android studio

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

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

Android之简单本地音乐播放器_第1张图片      

首先我是先做了一个大概的布局,样子先出来,需要其他的空间后期再添加,毕竟一开始不可能想的太详细,看一下主布局文件,就是一个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

Android之简单本地音乐播放器_第2张图片

第一点,列表上拉下滑时顶部和底部菜单栏消失,关键是要监听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



你可能感兴趣的:(android,音乐)