hunterliy小作品之 HunterMusic音乐播放器(Day2-后台播放服务实现)

来源:互联网 发布:数据库题库 编辑:程序博客网 时间:2024/04/30 01:56

1.1完成音乐播放布局

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/play_layout"    android:orientation="vertical"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="#e0e0e0"    tools:context="com.example.administrator.huntermusic.activity.AudioPlayActivity">    <TextView        android:id="@+id/music_name"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:gravity="center"        android:layout_gravity="center"        android:text="歌曲名"        android:textSize="20sp"        android:layout_marginTop="30dp"/>    <TextView        android:id="@+id/singer_name"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="歌手"        android:layout_margin="5dp"        android:layout_gravity="center"        android:gravity="center"/>    <com.example.administrator.huntermusic.utils.CDView        android:id="@+id/CD_view"        android:layout_marginBottom="80dp"        android:layout_marginTop="50dp"        android:layout_width="200dp"        android:layout_height="200dp"        android:layout_gravity="center">    </com.example.administrator.huntermusic.utils.CDView>    <TextView        android:id="@+id/tv_position"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="right"        android:text="00:00/00:00"        android:textSize="15sp"        android:layout_margin="10dp"        />    <SeekBar        android:id="@+id/seekbar"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginTop="6dp"        android:layout_marginLeft="4dp"        android:layout_marginRight="4dp"        android:layout_margin="20dp"        android:progress="0"        android:thumbOffset="0dp"        />    <LinearLayout        android:orientation="vertical"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginTop="4dp"        >        <LinearLayout            android:orientation="horizontal"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="center_horizontal"            android:gravity="center">            <ImageView                android:id="@+id/play_mode"                android:layout_width="32dp"                android:layout_height="wrap_content"                android:layout_gravity="center"                android:layout_margin="10dp"                />            <ImageView                android:id="@+id/pre"                android:layout_width="32dp"                android:layout_height="wrap_content"                android:layout_gravity="center"                android:layout_margin="10dp"                android:background="@drawable/pre"/>            <ImageView                android:id="@+id/play"                android:layout_width="64dp"                android:layout_height="wrap_content"                android:layout_margin="10dp"                />            <ImageView                android:id="@+id/next"                android:layout_width="32dp"                android:layout_height="wrap_content"                android:layout_gravity="center"                android:layout_margin="10dp"                android:background="@drawable/next"/>            <ImageView                android:id="@+id/playlist"                android:layout_width="32dp"                android:layout_height="wrap_content"                android:layout_gravity="center"                android:layout_margin="10dp"                android:background="@drawable/playlist"/>        </LinearLayout>    </LinearLayout></LinearLayout>

其中的CDView可以先使用一个ImageView来代替,之后我们再慢慢完善。最终效果如下:
这里写图片描述

1.2播放音乐

播放音乐需要在服务中播放,因为在退出界面的时候,音乐还在播放,这导致音乐的不可控制。所以为了便于控制我们将播放的逻辑放在服务中。那么就需要将数据也传递到服务中。

AudioPlayActivity .java

package com.example.administrator.huntermusic.activity;import android.content.BroadcastReceiver;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.content.ServiceConnection;import android.content.res.Resources;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.view.Window;public class AudioPlayActivity extends AppCompatActivity implements View.OnClickListener{    private AudioServiceConnection audioServiceConnection;    private AudioPlayService.AudioBinder binder;    @Override    protected void onCreate(Bundle savedInstanceState) {        requestWindowFeature(Window.FEATURE_NO_TITLE);        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_audio_play);        initData();        initView();    }    @Override    protected void onDestroy(){        super.onDestroy();        unbindService(audioServiceConnection);    }    public void initData(){        Intent service = new Intent(getIntent());        service.setClass(this, AudioPlayService.class);        audioServiceConnection = new AudioServiceConnection();        bindService(service, audioServiceConnection, BIND_AUTO_CREATE);        startService(service);    }    public class AudioServiceConnection implements ServiceConnection {        @Override        public void onServiceConnected(ComponentName name, IBinder iBinder) {               binder = (AudioPlayService.AudioBinder) iBinder;        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    }}

在服务中播放音乐

AudioPlayService .java

package com.example.administrator.huntermusic.service;import android.app.Service;import android.content.Intent;import android.media.MediaPlayer;import android.net.Uri;import android.os.Binder;import android.os.IBinder;import com.example.administrator.huntermusic.bean.AudioItem;import java.io.IOException;public class AudioPlayService extends Service {    private AudioBinder binder;    private List<AudioItem> audioItemList;    private int position;    @Override    public IBinder onBind(Intent intent) {        binder = new AudioBinder();        return binder;    }    @Override    public void onCreate() {        super.onCreate();    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        audioItemList = (List<AudioItem>) intent.getSerializableExtra("audioItems");        position = intent.getIntExtra("position", -1);        binder.play();        return super.onStartCommand(intent, flags, startId);    }    @Override    public boolean onUnbind(Intent intent) {        return super.onUnbind(intent);    }    @Override    public void onDestroy() {        super.onDestroy();    }    public class AudioBinder extends Binder{        private MediaPlayer media_player;        public void play(){            if (media_player!=null){                media_player.reset();                media_player.release();                media_player  = null;            }            AudioItem audioItem = audioItemList.get(position);            media_player = new MediaPlayer();            backIsPlay = true;            try {                media_player.setDataSource(AudioPlayService.this, Uri.parse(audioItem.getPath()));                media_player.prepare();                media_player.setOnPreparedListener(new OnAudioPreparedListener(media_player.getCurrentPosition()));                media_player.start();            } catch (IOException e) {                e.printStackTrace();            }    }}

1.3 完成界面功能

1.3.1在 AudioBinder 中提供音乐暂停开始的方法。在AudioPlayActivity 中点击开始按钮调用暂停开始的方法。

public void pause(){            media_player.pause();            backIsPlay = false;        }public void start(){            media_player.start();            backIsPlay = true;        }public boolean isPlaying(){            return media_player.isPlaying();        }

AudioPlayActivity 继承View .OnClickListener接口,在 AudioPlayActivity 中调用开始暂停播放的方法

@Override    public void onClick(View v) {        switch (v.getId()){            case R.id.play :                switchPlayState();                break;    }    private void switchPlayState() {        if (binder.isPlaying()){            binder.pause();            }else{            binder.start();        }        updataPlayButton();         }

更新播放按钮

private void updataPlayButton(){        if (binder.isPlaying()) {            play_button.setImageResource(R.drawable.pause_shadow);        } else {            play_button.setImageResource(R.drawable.play_shadow);        }    }

当音乐准备完成后需要改变界面按钮的状态为暂停按钮,并且初始化接收到的歌曲名和歌手名。在服务中发送广播,在界面中注册广播之后更新界面。

public class AudioBinder extends Binder{        private MediaPlayer media_player;        public void play(){            if (media_player!=null){                media_player.reset();                media_player.release();                media_player  = null;            }            AudioItem audioItem = audioItemList.get(position);            media_player = new MediaPlayer();            backIsPlay = true;            try {                media_player.setDataSource(AudioPlayService.this, Uri.parse(audioItem.getPath()));                media_player.prepare();                media_player.setOnPreparedListener(new OnAudioPreparedListener(media_player.getCurrentPosition()));                media_player.start();            } catch (IOException e) {                e.printStackTrace();            }            public void pause(){            media_player.pause();            backIsPlay = false;        }public void start(){            media_player.start();            backIsPlay = true;        }public boolean isPlaying(){            return media_player.isPlaying();        }private class OnAudioPreparedListener implements MediaPlayer.OnPreparedListener{            @Override            public void onPrepared(MediaPlayer mp) {            mp.start();                Intent intent = new Intent("com.work.huntermusic.updateplaybtn");                //获取当前正在播放的音乐                AudioItem audioItem = audioItemList.get(position);                intent.putExtra("audioItem",audioItem);                sendBroadcast(intent);            }        }}

在 AudioPlayActivity 中注册广播,更新界面

IntentFilter intentFilter = new IntentFilter("com.work.huntermusic.updateplaybtn");        registerReceiver(new AudioBroadCastReceiver(), intentFilter);private class AudioBroadCastReceiver extends BroadcastReceiver{        @Override        public void onReceive(Context context, Intent intent) {            //更新界面的按钮            updataPlayButton();            AudioItem audioItem = (AudioItem) intent.getSerializableExtra("audioItem");            music_name.setText(audioItem.getName());            singer_name.setText(audioItem.getSinger());     }}        

1.3.2时间进度更新
在服务中的 AudioBinder中提供获取播放总时长和当前播放进度的方法, 当 MediaPlayer准备完成后播放界面收到广播发消息开始更新

在 AudioBinder 中提供获取总时长和当前播放进度的方法

/** 获取音乐的播放总时长*/public int getDuration(){            return media_player.getDuration();        }/** 获取音乐当前播放进度*/public int getCurrentPosition(){            return media_player.getCurrentPosition();        }

在播放界面收到广播之后开始发消息更新

private class AudioBroadCastReceiver extends BroadcastReceiver{        @Override        public void onReceive(Context context, Intent intent) {            //更新界面的按钮            updataPlayButton();            AudioItem audioItem = (AudioItem) intent.getSerializableExtra("audioItem");            music_name.setText(audioItem.getName());            singer_name.setText(audioItem.getSinger());            //更新播放进度            updateCurrentPosition();     }} public void updataCurrentPosition(){        int duration=binder.getDuration();        int position=binder.getCurrentPosition();        seekbar.setMax(binder.getDuration());        String durationStr= StringUtil.formatDuration(duration);        String positionStr=StringUtil.formatDuration(position);        tv_position.setText(positionStr + " / " + durationStr);       handler.sendEmptyMessageDelayed(UPDATA_POSITION,500);    }    private static final int UPDATA_POSITION = 0;    private Handler handler = new Handler(){        @Override        public void handleMessage(Message msg) {            switch (msg.what){                case UPDATA_POSITION:                    updataCurrentPosition();        break;            }        }    };

这里给出StringUtil中时间转换的方法formatDuration()

public static String formatDuration(int duration){        int hour = 60*60*1000;        int minute = 60*1000;        int second = 1000;        int ghour = duration/hour;        int temp = duration%hour;        int gminute = temp/minute;        temp = temp%minute;        int gsecond = temp/second;        if (ghour==0){            return String.format("%02d:%02d",gminute,gsecond);        }else{            return String.format("%02d:%02d:%2d",ghour,gminute,gsecond);        }    }

为了防止内存泄露,在界面销毁的时候也需要将消息都移除掉。

1.3.3时间进度条的实现

在播放页面收到广播之后给 SeekBar 进行设置最大进度,在更新播放进度的时候同时去更新当前进度并且滑动 SeekBar 的时候改变进度

设置当前播放进度

 /** 更新播放进度*/ public void updateCurrentPosition() { int position=binder.getCurrentPosition(); //及时更新进度时间 updatePosition(position); handler.sendEmptyMessageDelayed(UPDATE_POSITION,500); }

滑动 SeekBar 的时候改变进度。在服务中提供改变进度的方法并且给 SeekBar 设置进度改变的监听

 seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {            @Override            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {                if (fromUser) {                    binder.seekTo(progress);                    updatePosition(progress);                }            }            @Override            public void onStartTrackingTouch(SeekBar seekBar) {            }            @Override            public void onStopTrackingTouch(SeekBar seekBar) {                binder.seekTo(seekBar.getProgress());            }        });

服务 AudioBinder 中的改变进度的方法

/** 进度跳转*/ public void seekTo(int progress){     mediaPlayer.seekTo(progress); }

1.3.4播放上一首和下一首

在 AudioBinder 中提供播放上一首和下一首的方法,当点击按钮的时候调用播放上一首和下一首的方法。注意:在播放的时候需要将之前的 mediaplayer 进行重置

        public void previous(){            if (position!=0){                position--;                media_player.reset();                play();            }else {                position = audioItemList.size()-1;            }        }        public void next(){            if (position!=audioItemList.size()-1){                position++;                media_player.reset();                play();            }else {                position = 0;            }        }

然后在播放页面调用binder的方法前一首和后一首的方法

@Override    public void onClick(View v) {        switch (v.getId()){            case R.id.play :                switchPlayState();                break;            case R.id.pre :                binder.previous();                break;            case R.id.next :                binder.next();                break;        }    }

1.3.5切换播放模式

在 AudioBinder 中提供切换播放模式的方法和获取当前播放模式

    public static final int PLAY_ALL_REPEAT = 0;    public static final int PLAY_RANDOM = 1;    public static final int PLAY_SINGLE_REPEAT = 2;    private int playMode = PLAY_ALL_REPEAT;        public void changePlayMode(){            if (playMode == PLAY_ALL_REPEAT){                    playMode = PLAY_RANDOM;            }else if (playMode ==PLAY_RANDOM) {                playMode = PLAY_SINGLE_REPEAT;            }else if (playMode ==PLAY_SINGLE_REPEAT){                    playMode = PLAY_ALL_REPEAT;            }        }

在播放页面点击切换播放模式的图片

@Override    public void onClick(View v) {        switch (v.getId()){            case R.id.play :                switchPlayState();                break;            case R.id.pre :                binder.previous();                break;            case R.id.next :                binder.next();                break;            case R.id.play_mode:                binder.changePlayMode();                //及时更新播放模式                updataPlayMode();                break;        }    }private void updataPlayMode() {        switch (binder.getPlayMode()){            case AudioPlayService.PLAY_ALL_REPEAT:                play_mode.setImageResource(R.drawable.list_repeat);                break;            case AudioPlayService.PLAY_RANDOM:                play_mode.setImageResource(R.drawable.random);                break;            case AudioPlayService.PLAY_SINGLE_REPEAT:                play_mode.setImageResource(R.drawable.single_repeat);                break;        }    }

1.3.6根据播放模式自动播放下一首音乐

在服务的 AudioBinder 的 play 方法中给 mediaplay 设置结束的监听,当音乐播放结束之后自动播放下一首

 media_player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {                @Override                public void onCompletion(MediaPlayer mp) {                //歌曲播放结束                //自动播放下一首                    autoPlayNext();                }            });private void autoPlayNext() {            switch (playMode){                case PLAY_ALL_REPEAT:                    if (position == audioItemList.size()-1) {                        position = 0;                    }else {                        position++;                    }                    break;                case PLAY_RANDOM:                    position = new Random().nextInt(audioItemList.size());                    break;                case PLAY_SINGLE_REPEAT:                    break;            }            play();        }

这里写图片描述
至此音乐播放服务和界面已经初步完成了,但是还有一些工作要作补充比如专辑图片和歌词等等,这个我会在之后进行补充,但是一个简略的MP3播放器已经成型,大家也可以根据我的代码自己加工构造出更好的作品,我只是作为抛砖引玉,同时希望也能得到大家的分享。

0 0