Android開發 本地與線上音樂播放器(基於Service實現)
專案裡需要做一個類似於QQ音樂,網易雲音樂一樣的實現本地與線上播放的音樂播發器。
本地的好做,查詢安卓自己的媒體庫ContentProvidre返回Cursor,一個個讀出來就好了。
關鍵是線上播放。
一開始在網上搜了搜Demo.找到一個線上播放的Demo.但看了看原始碼,線上播放那一部分是在Activity裡new 一個Thread,在Thread裡實現播放。
這樣做盡管能實現線上播放,但是和Activity的通訊變成了麻煩。畢竟還需要和Seekbar互動,實現拖拽進度,暫停,繼續等這樣的操作。
於是,自己在Service裡寫了一個線上播放。效果還不錯。‘
先看下最後的結果圖:
1.本地播放:
歌曲列表
播放介面,模仿網易雲音樂,背景根據專輯圖片高斯模糊
線上歌曲列表
2.線上播放,SeekBar裡的紅色代表當前進度,灰色代表線上緩衝進度,透明代表沒有緩衝到的。
接下來一步步說方法。
首先是本地播放:
根據ContentProvider查詢出一個包含所有本地音樂的Cursor.
1.用來獲取所有本地歌曲的MediaUtil.
MediaUtil.java
package com.lzw.util; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.provider.MediaStore; import android.util.Log; import com.lzw.bean.SongBean; import com.lzw.lmusicplayer.R; public class MediaUtil { private static final Uri albumArtUri = Uri.parse("content://media/external/audio/albumart"); public static ArrayList<SongBean> getAllSongs(Context context) { Cursor cursor = context.getContentResolver().query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER); ArrayList<SongBean> mp3Infos = new ArrayList<SongBean>(); for (int i = 0; i < cursor.getCount(); i++) { cursor.moveToNext(); SongBean mp3Info = new SongBean(); long id = cursor.getLong(cursor .getColumnIndex(MediaStore.Audio.Media._ID)); String title = cursor.getString((cursor .getColumnIndex(MediaStore.Audio.Media.TITLE))); String artist = cursor.getString(cursor .getColumnIndex(MediaStore.Audio.Media.ARTIST)); String album = cursor.getString(cursor .getColumnIndex(MediaStore.Audio.Media.ALBUM)); String displayName = cursor.getString(cursor .getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME)); long albumId = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID)); long duration = cursor.getLong(cursor .getColumnIndex(MediaStore.Audio.Media.DURATION)); long size = cursor.getLong(cursor .getColumnIndex(MediaStore.Audio.Media.SIZE)); String url = cursor.getString(cursor .getColumnIndex(MediaStore.Audio.Media.DATA)); int isMusic = cursor.getInt(cursor .getColumnIndex(MediaStore.Audio.Media.IS_MUSIC)); if (isMusic != 0) { mp3Info.setId(id); mp3Info.setTitle(title); mp3Info.setArtist(artist); mp3Info.setAlbum(album); mp3Info.setDisplayName(displayName); mp3Info.setAlbumId(albumId); mp3Info.setDuration(duration); mp3Info.setSize(size); mp3Info.setUrl(url); mp3Infos.add(mp3Info); } } return mp3Infos; } public static List<HashMap<String, String>> getMusicMaps( List<SongBean> mp3Infos) { List<HashMap<String, String>> mp3list = new ArrayList<HashMap<String, String>>(); for (Iterator iterator = mp3Infos.iterator(); iterator.hasNext();) { SongBean mp3Info = (SongBean) iterator.next(); HashMap<String, String> map = new HashMap<String, String>(); map.put("title", mp3Info.getTitle()); map.put("Artist", mp3Info.getArtist()); map.put("album", mp3Info.getAlbum()); map.put("displayName", mp3Info.getDisplayName()); map.put("albumId", String.valueOf(mp3Info.getAlbumId())); map.put("duration", formatTime(mp3Info.getDuration())); map.put("size", String.valueOf(mp3Info.getSize())); map.put("url", mp3Info.getUrl()); mp3list.add(map); } return mp3list; } public static String formatTime(long time) { String min = time / (1000 * 60) + ""; String sec = time % (1000 * 60) + ""; if (min.length() < 2) { min = "0" + time / (1000 * 60) + ""; } else { min = time / (1000 * 60) + ""; } if (sec.length() == 4) { sec = "0" + (time % (1000 * 60)) + ""; } else if (sec.length() == 3) { sec = "00" + (time % (1000 * 60)) + ""; } else if (sec.length() == 2) { sec = "000" + (time % (1000 * 60)) + ""; } else if (sec.length() == 1) { sec = "0000" + (time % (1000 * 60)) + ""; } return min + ":" + sec.trim().substring(0, 2); } private static final Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart"); private static final BitmapFactory.Options sBitmapOptions = new BitmapFactory.Options(); public static Bitmap getArtwork(Context context, long song_id, long album_id, boolean allowdefault) { if (album_id < 0) { if (song_id >= 0) { Bitmap bm = getArtworkFromFile(context, song_id, -1); if (bm != null) { return bm; } } if (allowdefault) { return getDefaultArtwork(context); } return null; } ContentResolver res = context.getContentResolver(); Uri uri = ContentUris.withAppendedId(sArtworkUri, album_id); if (uri != null) { InputStream in = null; try { in = res.openInputStream(uri); Bitmap bmp = BitmapFactory.decodeStream(in, null, sBitmapOptions); if (bmp == null) { bmp = getDefaultArtwork(context); } return bmp; } catch (FileNotFoundException ex) { Bitmap bm = getArtworkFromFile(context, song_id, album_id); if (bm != null) { if (bm.getConfig() == null) { bm = bm.copy(Bitmap.Config.RGB_565, false); if (bm == null && allowdefault) { return getDefaultArtwork(context); } } } else if (allowdefault) { bm = getDefaultArtwork(context); } return bm; } finally { try { if (in != null) { in.close(); } } catch (IOException e) { e.printStackTrace(); } } } return null; } private static Bitmap getArtworkFromFile(Context context, long songid, long albumid) { Bitmap bm = null; if (albumid < 0 && songid < 0) { throw new IllegalArgumentException("Must specify an album or a song id"); } try { if (albumid < 0) { Uri uri = Uri.parse("content://media/external/audio/media/" + songid + "/albumart"); ParcelFileDescriptor pfd = context.getContentResolver() .openFileDescriptor(uri, "r"); if (pfd != null) { FileDescriptor fd = pfd.getFileDescriptor(); bm = BitmapFactory.decodeFileDescriptor(fd); } } else { Uri uri = ContentUris.withAppendedId(sArtworkUri, albumid); ParcelFileDescriptor pfd = context.getContentResolver() .openFileDescriptor(uri, "r"); if (pfd != null) { FileDescriptor fd = pfd.getFileDescriptor(); bm = BitmapFactory.decodeFileDescriptor(fd); } } } catch (FileNotFoundException ex) { } return bm; } private static Bitmap getDefaultArtwork(Context context) { BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inPreferredConfig = Bitmap.Config.RGB_565; return BitmapFactory.decodeStream( context.getResources().openRawResource(R.drawable.default_album), null, opts); } }
2.在列表中展示的Activity.
這裡面有兩點要說。一個是因為考慮到一些發燒友歌曲量過大,1000+以上首歌曲,我在載入的時候,使用GifView顯示一張動態的Loading的GIF圖片,提高體驗度。載入要寫非同步任務。
第二點是,因為本地和線上是兩個不同的Service,所以在start其中一個Service的時候,要stop另一個Service,不然若此時線上的Service也在執行,則會出現本地和線上兩首歌同時播放的現象。
ListActivity.java
package com.lzw.activity; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import com.ant.liao.GifView; import com.google.gson.Gson; import com.lzw.bean.SongBean; import com.lzw.lmusicplayer.R; import com.lzw.service.NetPlayerService; import com.lzw.service.PlayerService; import com.lzw.util.MediaUtil; import java.util.ArrayList; public class ListActivity extends Activity implements ListView.OnItemClickListener{ private ListView musicListView; private ArrayList<SongBean> arrayList; private MusicListAdapter musicListAdapter; private GifView loadingGifView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_list); musicListView= (ListView) this.findViewById(R.id.musiclist); loadingGifView= (GifView) this.findViewById(R.id.loadinggif); loadingGifView.setGifImage(R.drawable.loading); new GetSystemMusicDataTask().execute(); musicListView.setOnItemClickListener(this); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Intent intent=new Intent(); intent.setClass(ListActivity.this, NetPlayerService.class); intent.setAction("com.lzw.media.NET_MUSIC_SERVICE"); stopService(intent); Intent intent2=new Intent(ListActivity.this,PlayActivity.class); intent2.putExtra("position",position); startActivity(intent2); } public class GetSystemMusicDataTask extends AsyncTask<Void,Void,Void>{ private String musicjsonstr; @Override protected Void doInBackground(Void... params) { arrayList=MediaUtil.getAllSongs(ListActivity.this); return null; } @Override protected void onPreExecute() { arrayList=new ArrayList<SongBean>(); loadingGifView.showAnimation(); } @Override protected void onPostExecute(Void aVoid) { loadingGifView.clearAnimation(); loadingGifView.setVisibility(View.GONE); musicListAdapter=new MusicListAdapter(arrayList); musicListView.setAdapter(musicListAdapter); } } public class MusicListAdapter extends BaseAdapter{ private ArrayList<SongBean> musicBeanArrayList; public MusicListAdapter(ArrayList arrayList){ musicBeanArrayList=arrayList; } @Override public int getCount() { return musicBeanArrayList.size(); } @Override public Object getItem(int position) { return musicBeanArrayList.get(position); } @Override public long getItemId(int position) { return musicBeanArrayList.get(position).getId(); } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if(convertView==null){ convertView= LayoutInflater.from(ListActivity.this).inflate(R.layout.item_song,null); holder = new ViewHolder(); holder.nameTextView= (TextView) convertView.findViewById(R.id.name); holder.singerTextView= (TextView) convertView.findViewById(R.id.singer); convertView.setTag(holder); }else{ holder = (ViewHolder)convertView.getTag(); } holder.singerTextView.setText(musicBeanArrayList.get(position).getArtist()); holder.nameTextView.setText(musicBeanArrayList.get(position).getTitle()); return convertView; } } public static class ViewHolder { public TextView nameTextView; public TextView singerTextView; } }
3.本地播放的Activity.
PlayActivity.java
在這個Activity裡,通過獲取專輯圖片Bitmap,使用高斯模糊演算法,生成模糊後的背景Bitmap,並設定。
註冊廣播,接受從Service傳來的總時長,當前進度等。
在使用者按下暫定按鈕,上一首,下一首按鈕的時候,給Service發MSG,更新Servie裡的MediaPlayer操作。
package com.lzw.activity;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import com.lzw.bean.SongBean;
import com.lzw.lmusicplayer.R;
import com.lzw.service.PlayerService;
import com.lzw.util.AppConstantUtil;
import com.lzw.util.GaussianBlurUtil;
import com.lzw.util.MediaUtil;
import com.lzw.view.CircleImageView;
public class PlayActivity extends Activity implements View.OnClickListener {
private Button mPlayPause;
private RelativeLayout mBackground ;
private CircleImageView mAvatar;
private boolean isPlaying;
private boolean isPause;
private Button mNext;
private Button mPrevious;
private Bitmap mResource;
private static int mCurrentPosition=-1;
private Boolean flag;
private String url;
private SongBean mSongBean;
private ArrayList<SongBean> musicInfos;
private TextView nameTextView,singerTextView,currentProgress,finalProgress;
private SeekBar music_progressBar;
private int currentTime;
private PlayerReceiver playerReceiver;
public static final String UPDATE_ACTION = "com.lzw.action.UPDATE_ACTION";
public static final String CTL_ACTION = "com.lzw.action.CTL_ACTION";
public static final String MUSIC_CURRENT = "com.lzw.action.MUSIC_CURRENT";
public static final String MUSIC_DURATION = "com.lzw.action.MUSIC_DURATION";
public static final String MUSIC_PLAYING = "com.lzw.action.MUSIC_PLAYING";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play);
Intent intent=getIntent();
int position=intent.getIntExtra("position", -1);
if(position!=mCurrentPosition){
mCurrentPosition=position;
flag=true;
isPause=false;
isPlaying=true;
}else {
flag=false;
isPlaying=true;
isPause=false;
}
musicInfos=MediaUtil.getAllSongs(this);
mSongBean=musicInfos.get(mCurrentPosition);
mPlayPause = (Button) findViewById(R.id.btn_play_pause);
music_progressBar=(SeekBar) this.findViewById(R.id.audioTrack);
mBackground = (RelativeLayout) findViewById(R.id.bg);
mAvatar = (CircleImageView) findViewById(R.id.avatar);
nameTextView=(TextView) this.findViewById(R.id.name);
singerTextView=(TextView) this.findViewById(R.id.singer);
currentProgress = (TextView) findViewById(R.id.current_progress);
finalProgress = (TextView) findViewById(R.id.final_progress);
mPlayPause.setOnClickListener(this);
mNext = (Button) findViewById(R.id.btn_next);
mNext.setOnClickListener(this);
mPrevious = (Button) findViewById(R.id.btn_previous);
mPrevious.setOnClickListener(this);
mResource = MediaUtil.getArtwork(this, mSongBean.getId(), mSongBean.getAlbumId(),true);
setViewContent(mResource);
nameTextView.setText(mSongBean.getTitle());
singerTextView.setText(mSongBean.getArtist());
music_progressBar.setMax((int) mSongBean.getDuration());
finalProgress.setText(MediaUtil.formatTime(mSongBean.getDuration()));
music_progressBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// TODO Auto-generated method stub
if (fromUser) {
mPlayPause.setBackgroundResource(R.drawable.fm_btn_pause);
isPlaying=true;
isPause=false;
audioTrackChange(progress);
}
}
});
playerReceiver = new PlayerReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(UPDATE_ACTION);
filter.addAction(MUSIC_CURRENT);
filter.addAction(MUSIC_DURATION);
registerReceiver(playerReceiver, filter);
if(flag){
play();
isPlaying=true;
isPause=false;
}else{
Intent intent2=new Intent(this,PlayerService.class);
intent2.putExtra("MSG", AppConstantUtil.PlayerMsg.PLAYING_MSG);
intent2.putExtra("position", mCurrentPosition);
intent2.setAction("com.lzw.media.MUSIC_SERVICE");
startService(intent2);
isPlaying=true;
isPause=false;
}
if(isPause){
mPlayPause.setBackgroundResource(R.drawable.fm_btn_play);
}else{
mPlayPause.setBackgroundResource(R.drawable.fm_btn_pause);
}
}
public void setViewContent(Bitmap bitmap){
setBackgroundBitmap(bitmap);
setAvatarBitmap(bitmap);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_play_pause:
if(isPlaying){
pause();
}else if (isPause){
resume();
}else{
play();
}
break;
case R.id.btn_previous:
mCurrentPosition--;
if(mCurrentPosition>=0) {
mSongBean=musicInfos.get(mCurrentPosition);
nameTextView.setText(mSongBean.getTitle());
singerTextView.setText(mSongBean.getArtist());
music_progressBar.setProgress(0);
mResource = MediaUtil.getArtwork(this, mSongBean.getId(), mSongBean.getAlbumId(), true);
previous(mResource);
if (isPlaying) {
mPlayPause.setBackgroundResource(R.drawable.fm_btn_pause);
} else {
mPlayPause.setBackgroundResource(R.drawable.fm_btn_play);
}
}else {
mCurrentPosition=0;
Toast.makeText(PlayActivity.this, "沒有上一首了", Toast.LENGTH_SHORT).show();
}
break;
case R.id.btn_next:
mCurrentPosition++;
if(mCurrentPosition<musicInfos.size()) {
mSongBean=musicInfos.get(mCurrentPosition);
nameTextView.setText(mSongBean.getTitle());
music_progressBar.setProgress(0);
singerTextView.setText(mSongBean.getArtist());
mResource = MediaUtil.getArtwork(this, mSongBean.getId(), mSongBean.getAlbumId(), true);
next(mResource);
if (isPlaying) {
mPlayPause.setBackgroundResource(R.drawable.fm_btn_pause);
} else {
mPlayPause.setBackgroundResource(R.drawable.fm_btn_play);
}
}else{
mCurrentPosition = musicInfos.size() - 1;
Toast.makeText(PlayActivity.this, "沒有下一首了", Toast.LENGTH_SHORT)
.show();
}
break;
default:
break;
}
}
public void setBackgroundBitmap(Bitmap bitmap){
mBackground.setBackgroundDrawable(GaussianBlurUtil.BoxBlurFilter(bitmap));
}
public void play(){
Intent intent = new Intent();
intent.setAction("com.lzw.media.MUSIC_SERVICE");
intent.setClass(this, PlayerService.class);
intent.putExtra("url", mSongBean.getUrl());
intent.putExtra("position", mCurrentPosition);
intent.putExtra("MSG", AppConstantUtil.PlayerMsg.PLAY_MSG);
startService(intent);
mPlayPause.setBackgroundResource(R.drawable.fm_btn_pause);
isPlaying=true;
isPause=false;
}
public void pause(){
Intent intent=new Intent();
intent.setClass(PlayActivity.this, PlayerService.class);
intent.setAction("com.lzw.media.MUSIC_SERVICE");
intent.putExtra("MSG", AppConstantUtil.PlayerMsg.PAUSE_MSG);
startService(intent);
isPlaying = false;
isPause = true;
mPlayPause.setBackgroundResource(R.drawable.fm_btn_play);
}
private void resume(){
Intent intent=new Intent();
intent.setAction("com.lzw.media.MUSIC_SERVICE");
intent.setClass(PlayActivity.this, PlayerService.class);
intent.putExtra("MSG", AppConstantUtil.PlayerMsg.CONTINUE_MSG);
startService(intent);
isPause = false;
isPlaying = true;
mPlayPause.setBackgroundResource(R.drawable.fm_btn_pause);
}
public void previous(Bitmap bitmap){
//pause();
changeImage(bitmap);
play();
}
public void next(Bitmap bitmap) {
//pause();
changeImage(bitmap);
play();
}
public void setAvatarBitmap(Bitmap bitmap){
mAvatar.setImageBitmap(bitmap);
}
private void changeImage(final Bitmap bitmap){
mAvatar.postDelayed(new Runnable() {
@Override
public void run() {
setAvatarBitmap(bitmap);
}
}, 100);
mBackground.postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
setBackgroundBitmap(bitmap);
}
}, 100);
}
public void audioTrackChange(int progress) {
Intent intent = new Intent();
intent.setClass(this, PlayerService.class);
intent.setAction("com.lzw.media.MUSIC_SERVICE");
intent.putExtra("url", mSongBean.getUrl());
intent.putExtra("MSG", AppConstantUtil.PlayerMsg.PROGRESS_CHANGE);
intent.putExtra("position", mCurrentPosition);
intent.putExtra("progress", progress);
startService(intent);
}
public class PlayerReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(MUSIC_CURRENT)) {
currentTime = intent.getIntExtra("currentTime", -1);
currentProgress.setText(MediaUtil.formatTime(currentTime));
music_progressBar.setProgress(currentTime);
} else if (action.equals(MUSIC_DURATION)) {
int duration = intent.getIntExtra("duration", -1);
music_progressBar.setMax(duration);
finalProgress.setText(MediaUtil.formatTime(duration));
} else if (action.equals(UPDATE_ACTION)) {
mCurrentPosition = intent.getIntExtra("current", -1);
mSongBean=musicInfos.get(mCurrentPosition);
url = mSongBean.getUrl();
if (mCurrentPosition >= 0) {
nameTextView.setText(mSongBean.getTitle());
singerTextView.setText(mSongBean.getArtist());
mResource = MediaUtil.getArtwork(PlayActivity.this, mSongBean.getId(), mSongBean.getAlbumId(),true);
setViewContent(mResource);
}
if (mCurrentPosition == 0) {
finalProgress.setText(MediaUtil.formatTime(musicInfos.get(
mCurrentPosition).getDuration()));
mPlayPause.setBackgroundResource(R.drawable.fm_btn_play);
isPause = true;
}
}
}
}
}
4.PlayerServie.java
這個是實現本地播放的Service;
在Servie裡既要在Handler裡給Activity發廣播,傳遞當前的播放進度和總時長。
又要接受Activity發來的Intent裡的動作訊息,切換播放狀態。
為了實現更新進度,我在Handler裡進行訊息迴圈,每隔1000毫秒給PlayActiviy傳送一次廣播,更新當前的進度。
package com.lzw.service;
import java.util.ArrayList;
import com.lzw.bean.SongBean;
import com.lzw.util.AppConstantUtil;
import com.lzw.util.MediaUtil;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
public class PlayerService extends Service{
private MediaPlayer mediaPlayer;
private String path;
private int msg;
private boolean isPause;
private int mCurrentPosition;
private int currentTime;
private int duration;
private ArrayList<SongBean> mp3Infos;
public static final String UPDATE_ACTION = "com.lzw.action.UPDATE_ACTION";
public static final String CTL_ACTION = "com.lzw.action.CTL_ACTION";
public static final String MUSIC_CURRENT = "com.lzw.action.MUSIC_CURRENT";
public static final String MUSIC_DURATION = "com.lzw.action.MUSIC_DURATION";
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == 1) {
if(mediaPlayer != null) {
currentTime = mediaPlayer.getCurrentPosition(); // 獲取當前音樂播放的位置
Intent intent = new Intent();
intent.setAction(MUSIC_CURRENT);
intent.putExtra("currentTime", currentTime);
sendBroadcast(intent); // 給PlayerActivity傳送廣播
handler.sendEmptyMessageDelayed(1, 1000);
}
}
};
};
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d("service", "service created");
mediaPlayer = new MediaPlayer();
mp3Infos = MediaUtil.getAllSongs(PlayerService.this);
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub
mCurrentPosition++;
Log.v("po", mCurrentPosition+"");
if (mCurrentPosition <= mp3Infos.size() - 1) {
Intent sendIntent = new Intent(UPDATE_ACTION);
sendIntent.putExtra("current", mCurrentPosition);
sendBroadcast(sendIntent);
path = mp3Infos.get(mCurrentPosition).getUrl();
play(0);
}else {
mediaPlayer.seekTo(0);
mCurrentPosition = 0;
Intent sendIntent = new Intent(UPDATE_ACTION);
sendIntent.putExtra("current", mCurrentPosition);
sendBroadcast(sendIntent);
}
}
});
}
@Override
public void onStart(Intent intent, int startId) {
if(intent==null){
stopSelf();
}else{
path = intent.getStringExtra("url");
msg = intent.getIntExtra("MSG", 0);
mCurrentPosition=intent.getIntExtra("position",-1);
if (msg == AppConstantUtil.PlayerMsg.PLAY_MSG) {
play(0);
} else if (msg == AppConstantUtil.PlayerMsg.PAUSE_MSG) {
pause();
} else if (msg == AppConstantUtil.PlayerMsg.STOP_MSG) {
stop();
} else if (msg == AppConstantUtil.PlayerMsg.CONTINUE_MSG) {
resume();
} else if (msg == AppConstantUtil.PlayerMsg.PROGRESS_CHANGE) {
currentTime = intent.getIntExtra("progress", -1);
play(currentTime);
} else if (msg == AppConstantUtil.PlayerMsg.PLAYING_MSG) {
handler.sendEmptyMessage(1);
}
super.onStart(intent, startId);
}
}
private void play(int currentTime) {
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(path);
mediaPlayer.prepare();
mediaPlayer.setOnPreparedListener(new PreparedListener(currentTime));
handler.sendEmptyMessage(1);
} catch (Exception e) {
e.printStackTrace();
}
}
private void pause() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
isPause = true;
}
}
private void resume() {
if (isPause) {
mediaPlayer.start();
isPause = false;
}
}
private void stop() {
if (mediaPlayer != null) {
mediaPlayer.stop();
try {
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private final class PreparedListener implements OnPreparedListener {
private int currentTime;
public PreparedListener(int currentTime) {
this.currentTime = currentTime;
}
@Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
if (currentTime > 0) {
mediaPlayer.seekTo(currentTime);
}
Intent intent = new Intent();
intent.setAction(MUSIC_DURATION);
duration = mediaPlayer.getDuration();
intent.putExtra("duration", duration);
sendBroadcast(intent);
}
}
@Override
public void onDestroy() {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
}
}
5.儲存歌曲實體的Bean
package com.lzw.bean;
import java.io.Serializable;
public class SongBean implements Serializable {
private long id;
private String title;
private String album;
private long albumId;
private String displayName;
private String artist;
private long duration;
private long size;
private String url;
public SongBean() {
}
public SongBean(long id, String title, String album, long albumId,
String displayName, String artist, long duration, long size,
String url, String lrcTitle, String lrcSize) {
super();
this.id = id;
this.title = title;
this.album = album;
this.albumId = albumId;
this.displayName = displayName;
this.artist = artist;
this.duration = duration;
this.size = size;
this.url = url;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAlbum() {
return album;
}
public void setAlbum(String album) {
this.album = album;
}
public long getAlbumId() {
return albumId;
}
public void setAlbumId(long albumId) {
this.albumId = albumId;
}
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
public long getDuration() {
return duration;
}
public void setDuration(long duration) {
this.duration = duration;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
}
本地音樂的關鍵程式碼就是這些。
接下來看線上播放的關鍵部分。
首先,服務端的測試介面是我自己寫的
需要的注意的是,獲得out物件是要在設定了response的編碼後再獲得,一定要注意這一點,不然會出現中文亂碼的情況。
為了下拉重新整理與上拉載入的功能,我設了一個page引數。
Server部分
IMusicList.java
package com.lzw.mobVod.interfac;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.gson.Gson;
import com.lzw.mobVod.bean.MusicBean;
public class IMusicList extends HttpServlet{
/* (non-Javadoc)
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
resp.setContentType("text/html;charset=UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setHeader("ContentType","text/html;charset=UTF-8");
req.setCharacterEncoding("UTF-8");
PrintWriter out=resp.getWriter();
Gson gson=new Gson();
ArrayList<MusicBean> arrayList=new ArrayList<>();
int page=Integer.parseInt(req.getParameter("page"));
for(int i=10*(page-1);i<10*page;i++) {
MusicBean bean=new MusicBean();
bean.setMusic_name("陌生人"+i);
bean.setMusic_singer("蔡健雅"+i);
bean.setMusic_id(i+"");
bean.setMusic_cover_url("http://xxx.xx.xx.xxx:8080/mobVod/img/music_icon.jpg");//改成自己的伺服器地址
bean.setMusic_url("http://xxx.xx.xx.xx:8080/mobVod/music/hongsegaogenxie.mp3");
arrayList.add(bean);
}
String jsonstr=gson.toJson(arrayList);
out.print(jsonstr);
}
}
客戶端接收介面並在XListView中顯示,下拉重新整理與上拉載入
NetPlayActivity.java
package com.lzw.activity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.lzw.activity.NetPlayActivity.PlayerReceiver;
import com.lzw.bean.NetSongBean;
import com.lzw.lmusicplayer.R;
import com.lzw.service.PlayerService;
import com.lzw.util.AppConstantUtil;
import com.lzw.view.XListView;
import com.lzw.view.XListView.IXListViewListener;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class NetListActivity extends Activity{
private XListView musicListView;
private ArrayList<NetSongBean> arrayList;
private MusicListAdapter musicListAdapter;
private int page=1;
private final String URL="http://xxx.xx.xx.xxx:8080/mobVod/IMusicList.do";
private RequestQueue mRequestQueue;
public static final String MUSIC_DURATION = "com.lzw.action.NET_MUSIC_DURATION";//新音樂長度更新動作
private ListReceiver mListReceiver;
private Gson gson;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_netlist);
musicListView=(XListView) this.findViewById(R.id.musiclist);
arrayList=new ArrayList<NetSongBean>();
gson=new Gson();
musicListView.setPullLoadEnable(true);
musicListView.setPullRefreshEnable(true);
mRequestQueue = Volley.newRequestQueue(this);
getNetData();
musicListView.setXListViewListener(new IXListViewListener() {
@Override
public void onRefresh() {
// TODO Auto-generated method stub
page++;
getNetData();
}
@Override
public void onLoadMore() {
// TODO Auto-generated method stub
page++;
getNetData();
musicListView.smoothScrollToPosition(musicListView.getCount() - 1);
}
});
musicListView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// TODO Auto-generated method stub
Intent intent=new Intent();
intent.setClass(NetListActivity.this, PlayerService.class);
intent.setAction("com.lzw.media.MUSIC_SERVICE");
stopService(intent);
Intent intent2=new Intent(NetListActivity.this,NetPlayActivity.class);
intent2.putExtra("netmusicitem", arrayList.get(position-1));
intent2.putExtra("position", position-1);
startActivity(intent2);
}
});
mListReceiver = new ListReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(MUSIC_DURATION);
registerReceiver(mListReceiver, filter);
}
private void onLoad() {
musicListView.stopRefresh();
musicListView.stopLoadMore();
}
private void getNetData(){
RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
StringRequest stringRequest = new StringRequest(Request.Method.POST,URL,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("re", "response -> " + response);
arrayList.addAll((ArrayList<NetSongBean>)gson.fromJson(response, new TypeToken<ArrayList<NetSongBean>>(){}.getType()));
musicListAdapter=new MusicListAdapter(arrayList);
musicListView.setAdapter(musicListAdapter);
onLoad();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("er", error.getMessage(), error);
}
}) {
@Override
protected Map<String, String> getParams() {
//在這裡設定需要post的引數
Map<String, String> map = new HashMap<String, String>();
map.put("page", page+"");
return map;
}
};
requestQueue.add(stringRequest);
}
public class MusicListAdapter extends BaseAdapter{
private ArrayList<NetSongBean> musicBeanArrayList;
public MusicListAdapter(ArrayList arrayList){
musicBeanArrayList=arrayList;
}
@Override
public int getCount() {
return musicBeanArrayList.size();
}
@Override
public Object getItem(int position) {
return musicBeanArrayList.get(position);
}
@Override
public long getItemId(int position) {
return Integer.parseInt(musicBeanArrayList.get(position).getMusic_id());
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView==null){
convertView= LayoutInflater.from(NetListActivity.this).inflate(R.layout.item_song,null);
holder = new ViewHolder();
holder.nameTextView= (TextView) convertView.findViewById(R.id.name);
holder.singerTextView= (TextView) convertView.findViewById(R.id.singer);
convertView.setTag(holder);
}else{
holder = (ViewHolder)convertView.getTag();
}
holder.singerTextView.setText(musicBeanArrayList.get(position).getMusic_name());
holder.nameTextView.setText(musicBeanArrayList.get(position).getMusic_singer());
return convertView;
}
}
public static class ViewHolder {
public TextView nameTextView;
public TextView singerTextView;
}
public class ListReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String action=intent.getAction();
if(action.equals(MUSIC_DURATION)){
int pos=intent.getIntExtra("pos", -1);
int dura=intent.getIntExtra("duration", 1);
if(pos>0&&dura>0){
arrayList.get(pos).setDuration(dura);
}
}
}
}
}
可以發現,歌曲的時長在接口裡的json中並沒有資料,我通過在Service播放一首歌時,MediaPlayer獲得當前播放歌曲的時長,傳送廣播,讓兩個NetPlayActivity和NetListActivity都接受到,在播放介面的NetPlayActivity裡收到歌曲市場後,設定Textview的text值,同時為了保證在播放過程中下一次點同一個Postion能跳轉到之前的播放介面,讓資料保持之前的狀態,在ListActivity收到廣播後,更新對應的NetSongBean的duartion屬性。
儲存網路線上歌曲資訊的實體Bean
NetSongBean.java
package com.lzw.bean;
import java.io.Serializable;
public class NetSongBean implements Serializable {
private String music_id;
public String getMusic_id() {
return music_id;
}
public void setMusic_id(String music_id) {
this.music_id = music_id;
}
public String getMusic_name() {
return music_name;
}
public void setMusic_name(String music_name) {
this.music_name = music_name;
}
public String getMusic_cover_url() {
return music_cover_url;
}
public void setMusic_cover_url(String music_cover_url) {
this.music_cover_url = music_cover_url;
}
public String getMusic_url() {
return music_url;
}
public void setMusic_url(String music_url) {
this.music_url = music_url;
}
public String getMusic_singer() {
return music_singer;
}
public void setMusic_singer(String music_singer) {
this.music_singer = music_singer;
}
private String music_name;
private String music_cover_url;
private String music_url;
private String music_singer;
private int duration=-1;
public void setDuration(int duration) {
this.duration = duration;
}
public int getDuration() {
return duration;
}
}
接下來就是最關鍵的NetPlayService,實現了線上播放音樂的Service.
NetPlayService.java
這個Servce實現了介面OnBufferingUpdateListener。
當網路音訊開始載入的時候,每次更新一次進度,會在回掉函式裡更新一次當前的百分比。此時Service給Activity傳送廣播,根據緩衝百分比,歌曲總時長,來設定SeekBar的SecondaryProgress的值。同時,Servie裡接受使用者拖動進度條後的Progress值,Activity發給Service,Servie收到後,判斷這個值是否在已緩衝的範圍內,如果在的話,mediaplayer.seekto一下。實現拖動進度播放。
package com.lzw.service;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import com.lzw.util.AppConstantUtil;
import android.R.integer;
import android.app.Service;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
public class NetPlayerService extends Service implements OnBufferingUpdateListener, OnCompletionListener,
OnPreparedListener {
public MediaPlayer mediaPlayer;
private int currentTime;
private int duration;
private int mCurrentPosition;
private int msg;
private boolean isPause;
private String url;
private int percent;
public static final String MUSIC_CURRENT = "com.lzw.action.NET_MUSIC_CURRENT";
public static final String MUSIC_DURATION = "com.lzw.action.NET_MUSIC_DURATION";
public static final String MUSIC_PERCENT="com.lzw.action.NET_MUSIC_PERCENT";
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == 0) {
if(mediaPlayer != null) {
currentTime = mediaPlayer.getCurrentPosition();
Intent intent = new Intent();
intent.setAction(MUSIC_CURRENT);
intent.putExtra("currentTime", currentTime);
sendBroadcast(intent);
handler.sendEmptyMessageDelayed(0, 1000);
}
}
if(msg.what==1){
duration = mediaPlayer.getDuration();
if (duration > 0) {
Intent intent = new Intent();
intent.setAction(MUSIC_DURATION);
intent.putExtra("pos", mCurrentPosition);
Log.v("DUA", duration+"");
intent.putExtra("duration", duration);
sendBroadcast(intent);
}
}
if(msg.what==2){
Intent intent=new Intent();
intent.setAction(MUSIC_PERCENT);
intent.putExtra("percent", percent);
sendBroadcast(intent);
}
};
};
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
try {
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnPreparedListener(this);
} catch (Exception e) {
e.printStackTrace();
}
}
private void play(int currentTime) {
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(url);
mediaPlayer.prepare();
mediaPlayer.setOnPreparedListener(this);
handler.sendEmptyMessage(0);
handler.sendEmptyMessage(1);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onStart(Intent intent, int flags) {
// TODO Auto-generated method stub
if(intent==null){
stopSelf();
}
url=intent.getStringExtra("url");
msg = intent.getIntExtra("MSG", 0);
mCurrentPosition=intent.getIntExtra("position",-1);
if (msg == AppConstantUtil.PlayerMsg.PLAY_MSG) {
play(0);
} else if(msg==AppConstantUtil.PlayerMsg.PROGRESS_CHANGE){
currentTime = intent.getIntExtra("progress", -1);
if(currentTime>0&¤tTime<=(percent*duration/100)){
mediaPlayer.seekTo(currentTime);
handler.sendEmptyMessage(1);
//play(currentTime);
}
}else if (msg == AppConstantUtil.PlayerMsg.PLAYING_MSG) {
handler.sendEmptyMessage(0);
}else if (msg == AppConstantUtil.PlayerMsg.PAUSE_MSG) {
pause();
} else if (msg == AppConstantUtil.PlayerMsg.STOP_MSG) {
stop();
} else if (msg == AppConstantUtil.PlayerMsg.CONTINUE_MSG) {
resume();
}
return;
}
private void pause() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
isPause = true;
}
}
private void resume() {
if (isPause) {
mediaPlayer.start();
isPause = false;
}
}
/**
* 停止音樂
*/
private void stop() {
if (mediaPlayer != null) {
mediaPlayer.stop();
try {
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
Intent intent = new Intent();
intent.setAction(MUSIC_DURATION);
duration = mediaPlayer.getDuration();
intent.putExtra("duration", duration);
Log.v("duran", duration+"");
sendBroadcast(intent);
}
@Override
public void onCompletion(MediaPlayer mp) {
Log.e("mediaPlayer", "onCompletion");
}
@Override
public void onBufferingUpdate(MediaPlayer mp, int perc) {
percent=perc;
handler.sendEmptyMessage(2);
}
}
線上播放介面
NetPlayActivity.java
接收廣播與傳送廣播。
package com.lzw.activity;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageRequest;
import com.android.volley.toolbox.Volley;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.Transformation;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;
import com.lzw.activity.PlayActivity.PlayerReceiver;
import com.lzw.bean.NetSongBean;
import com.lzw.lmusicplayer.R;
import com.lzw.service.NetPlayerService;
import com.lzw.service.PlayerService;
import com.lzw.util.AppConstantUtil;
import com.lzw.util.GaussianBlurUtil;
import com.lzw.util.MediaUtil;
import com.lzw.util.NetPlayerUtil;
import com.lzw.view.CircleImageView;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.widget.SimpleCursorAdapter.ViewBinder;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.Toast;
public class NetPlayActivity extends Activity implements OnClickListener{
private static int mCurrentPosition=-1;
private Boolean flag;
private Button playBtn;
private RelativeLayout mBackground;
private CircleImageView mAvatar;
private SeekBar musicProgress;
private RequestQueue mQueue;
private boolean isPlaying;
private boolean isPause;
private NetSongBean mNetSongBean;
private int currentTime;
private TextView nameTextView,singerTextView,currentProgress,finalProgress;
public static final String UPDATE_ACTION = "com.lzw.action.NET_UPDATE_ACTION";
public static final String CTL_ACTION = "com.lzw.action.NET_CTL_ACTION";
public static final String MUSIC_CURRENT = "com.lzw.action.NET_MUSIC_CURRENT";
public static final String MUSIC_DURATION = "com.lzw.action.NET_MUSIC_DURATION";
public static final String MUSIC_PERCENT="com.lzw.action.NET_MUSIC_PERCENT";
private PlayerReceiver playerReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_netplay);
playBtn = (Button) findViewById(R.id.btn_online_play);
mBackground = (RelativeLayout) findViewById(R.id.bg);
mAvatar = (CircleImageView) findViewById(R.id.avatar);
nameTextView=(TextView) this.findViewById(R.id.name);
singerTextView=(TextView) this.findViewById(R.id.singer);
currentProgress = (TextView) findViewById(R.id.current_progress);
finalProgress = (TextView) findViewById(R.id.final_progress);
musicProgress = (SeekBar) findViewById(R.id.audioTrack);
Intent intent=getIntent();
mNetSongBean=(NetSongBean) intent.getSerializableExtra("netmusicitem");
int position=intent.getIntExtra("position", -1);
if(isPlaying){
if(position!=mCurrentPosition){
mCurrentPosition=position;
flag=true;
isPlaying=true;
isPause=false;
}else {
flag=false;
isPause=false;
isPlaying=true;
}
}else{
if(position!=mCurrentPosition){
mCurrentPosition=position;
flag=true;
isPlaying=true;
isPause=false;
}else {
flag=false;
isPause=true;
isPlaying=false;
}
}
nameTextView.setText(mNetSongBean.getMusic_name());
singerTextView.setText(mNetSongBean.getMusic_singer());
playBtn.setOnClickListener(this);
musicProgress.setOnSeekBarChangeListener(new SeekBarChangeEvent());
if(mNetSongBean.getDuration()>0){
finalProgress.setText(MediaUtil.formatTime(mNetSongBean.getDuration()));
musicProgress.setMax(mNetSongBean.getDuration());
}
Glide.with(this).load(mNetSongBean.getMusic_cover_url()).into(mAvatar);
mQueue=Volley.newRequestQueue(this);
mBackground.postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
ImageRequest imageRequest = new ImageRequest(
mNetSongBean.getMusic_cover_url(),
new Response.Listener<Bitmap>() {
@SuppressWarnings("deprecation")
@Override
public void onResponse(Bitmap response) {
//Glide.with(NetPlayActivity.this).load(mNetSongBean.getMusic_cover_url()).into(mAvatar);
mBackground.setBackgroundDrawable(GaussianBlurUtil.BoxBlurFilter(response));
}
}, 0, 0, Config.RGB_565, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
});
mQueue.add(imageRequest);
}
}, 1500);
playerReceiver = new PlayerReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(UPDATE_ACTION);
filter.addAction(MUSIC_CURRENT);
filter.addAction(MUSIC_DURATION);
filter.addAction(MUSIC_PERCENT);
registerReceiver(playerReceiver, filter);
if(flag){
play();
isPlaying=true;
isPause=false;
}else{
isPlaying=true;
isPause=false;
}
if(isPause){
playBtn.setBackgroundResource(R.drawable.fm_btn_play);
}else{
playBtn.setBackgroundResource(R.drawable.fm_btn_pause);
}
}
public class PlayerReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(MUSIC_CURRENT)) {
currentTime = intent.getIntExtra("currentTime", -1);
currentProgress.setText(MediaUtil.formatTime(currentTime));
musicProgress.setProgress(currentTime);
} else if (action.equals(MUSIC_DURATION)) {
int duration = intent.getIntExtra("duration", -1);
Log.v("dura", duration+"");
musicProgress.setMax(duration);
if(mNetSongBean.getDuration()==-1){
mNetSongBean.setDuration(duration);
}
finalProgress.setText(MediaUtil.formatTime(duration));
}else if(action.equals(MUSIC_PERCENT)){
int percent=intent.getIntExtra("percent",-1);
musicProgress.setSecondaryProgress(musicProgress.getMax()*percent/100);
}
}
}
private void play(){
Intent intent = new Intent();
intent.setAction("com.lzw.media.NET_MUSIC_SERVICE");
intent.setClass(this,NetPlayerService.class);
intent.putExtra("url", mNetSongBean.getMusic_url());
intent.putExtra("position", mCurrentPosition);
intent.putExtra("MSG", AppConstantUtil.PlayerMsg.PLAY_MSG);
startService(intent);
isPlaying=true;
isPause=false;
}
class SeekBarChangeEvent implements OnSeekBarChangeListener {
@Override
public void onProgressChanged(SeekBar seekBar, int progre,
boolean fromUser) {
currentTime=progre;
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
Intent intent = new Intent();
intent.setClass(NetPlayActivity.this, NetPlayerService.class);
intent.setAction("com.lzw.media.NET_MUSIC_SERVICE");
intent.putExtra("url", mNetSongBean.getMusic_url());
intent.putExtra("MSG", AppConstantUtil.PlayerMsg.PROGRESS_CHANGE);
intent.putExtra("position", mCurrentPosition);
intent.putExtra("progress", currentTime);
startService(intent);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
public void pause(){
Intent intent=new Intent();
intent.setClass(NetPlayActivity.this, NetPlayerService.class);
intent.setAction("com.lzw.media.NET_MUSIC_SERVICE");
intent.putExtra("MSG", AppConstantUtil.PlayerMsg.PAUSE_MSG);
startService(intent);
isPlaying = false;
isPause = true;
playBtn.setBackgroundResource(R.drawable.fm_btn_play);
}
private void resume(){
Intent intent=new Intent();
intent.setAction("com.lzw.media.NET_MUSIC_SERVICE");
intent.setClass(NetPlayActivity.this, NetPlayerService.class);
intent.putExtra("MSG", AppConstantUtil.PlayerMsg.CONTINUE_MSG);
startService(intent);
isPause = false;
isPlaying = true;
playBtn.setBackgroundResource(R.drawable.fm_btn_pause);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_online_play:
if(isPlaying&&!isPause){
pause();
Log.v("clickpause", isPlaying+""+isPause);
}else if (isPause&&!isPlaying){
resume();
Log.v("clickresume", isPlaying+""+isPause);
}else{
Log.v("clickplay", isPlaying+""+isPause);
play();
}
break;
}
}
}
整個做法大體就是這樣了,最後放一個高斯模糊演算法。
package com.lzw.util;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
public class GaussianBlurUtil {
private static float hRadius = 10;
private static float vRadius = 10;
private static int iterations = 7;
public static Drawable BoxBlurFilter(Bitmap bmp) {
int width = bmp.getWidth();
int height = bmp.getHeight();
int[] inPixels = new int[width * height];
int[] outPixels = new int[width * height];
Bitmap bitmap = Bitmap.createBitmap(width, height,Bitmap.Config.ARGB_8888);
bmp.getPixels(inPixels, 0, width, 0, 0, width, height);
for (int i = 0; i < iterations; i++) {
blur(inPixels, outPixels, width, height, hRadius);
blur(outPixels, inPixels, height, width, vRadius);
}
blurFractional(inPixels, outPixels, width, height, hRadius);
blurFractional(outPixels, inPixels, height, width, vRadius);
bitmap.setPixels(inPixels, 0, width, 0, 0, width, height);
Drawable drawable = new BitmapDrawable(bitmap);
return drawable;
}
public static Bitmap BoxBlurFilterBitmap(Bitmap bmp) {
int width = bmp.getWidth();
int height = bmp.getHeight();
int[] inPixels = new int[width * height];
int[] outPixels = new int[width * height];
Bitmap bitmap = Bitmap.createBitmap(width, height,Bitmap.Config.ARGB_8888);
bmp.getPixels(inPixels, 0, width, 0, 0, width, height);
for (int i = 0; i < iterations; i++) {
blur(inPixels, outPixels, width, height, hRadius);
blur(outPixels, inPixels, height, width, vRadius);
}
blurFractional(inPixels, outPixels, width, height, hRadius);
blurFractional(outPixels, inPixels, height, width, vRadius);
bitmap.setPixels(inPixels, 0, width, 0, 0, width, height);
return bitmap;
}
public static void blur(int[] in, int[] out, int width, int height,
float radius) {
int widthMinus1 = width - 1;
int r = (int) radius;
int tableSize = 2 * r + 1;
int divide[] = new int[256 * tableSize];
for (int i = 0; i < 256 * tableSize; i++)
divide[i] = i / tableSize;
int inIndex = 0;
for (int y = 0; y < height; y++) {
int outIndex = y;
int ta = 0, tr = 0, tg = 0, tb = 0;
for (int i = -r; i <= r; i++) {
int rgb = in[inIndex + clamp(i, 0, width - 1)];
ta += (rgb >> 24) & 0xff;
tr += (rgb >> 16) & 0xff;
tg += (rgb >> 8) & 0xff;
tb += rgb & 0xff;
}
for (int x = 0; x < width; x++) {
out[outIndex] = (divide[ta] << 24) | (divide[tr] << 16)
| (divide[tg] << 8) | divide[tb];
int i1 = x + r + 1;
if (i1 > widthMinus1)
i1 = widthMinus1;
int i2 = x - r;
if (i2 < 0)
i2 = 0;
int rgb1 = in[inIndex + i1];
int rgb2 = in[inIndex + i2];
ta += ((rgb1 >> 24) & 0xff) - ((rgb2 >> 24) & 0xff);
tr += ((rgb1 & 0xff0000) - (rgb2 & 0xff0000)) >> 16;
tg += ((rgb1 & 0xff00) - (rgb2 & 0xff00)) >> 8;
tb += (rgb1 & 0xff) - (rgb2 & 0xff);
outIndex += height;
}
inIndex += width;
}
}
public static void blurFractional(int[] in, int[] out, int width,
int height, float radius) {
radius -= (int) radius;
float f = 1.0f / (1 + 2 * radius);
int inIndex = 0;
for (int y = 0; y < height; y++) {
int outIndex = y;
out[outIndex] = in[0];
outIndex += height;
for (int x = 1; x < width - 1; x++) {
int i = inIndex + x;
int rgb1 = in[i - 1];
int rgb2 = in[i];
int rgb3 = in[i + 1];
int a1 = (rgb1 >> 24) & 0xff;
int r1 = (rgb1 >> 16) & 0xff;
int g1 = (rgb1 >> 8) & 0xff;
int b1 = rgb1 & 0xff;
int a2 = (rgb2 >> 24) & 0xff;
int r2 = (rgb2 >> 16) & 0xff;
int g2 = (rgb2 >> 8) & 0xff;
int b2 = rgb2 & 0xff;
int a3 = (rgb3 >> 24) & 0xff;
int r3 = (rgb3 >> 16) & 0xff;
int g3 = (rgb3 >> 8) & 0xff;
int b3 = rgb3 & 0xff;
a1 = a2 + (int) ((a1 + a3) * radius);
r1 = r2 + (int) ((r