失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 基于android的网络音乐播放器-播放控制界面(九)

基于android的网络音乐播放器-播放控制界面(九)

时间:2020-03-17 05:41:15

相关推荐

基于android的网络音乐播放器-播放控制界面(九)

到这里我们的音乐播放器已经有了播放,收藏,搜索网络音乐并下载(包括多线程断点下载)等基本功能,下面将开发播放界面——实现音乐播放的控制-上/下一首,播放/暂停,循环控制,专辑图片的加载,歌词的加载和解析并支持滑动改变进度。这些实现主要在一个类里-PlayMusicActivity.java

package com.sprd.easymusic;import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.UnsupportedEncodingException;import java.lang.ref.SoftReference;import .HttpURLConnection;import .MalformedURLException;import .URL;import .URLEncoder;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import org.json.JSONArray;import org.json.JSONException;import org.json.JSONObject;import com.android.volley.RequestQueue;import com.android.volley.Response;import com.android.volley.VolleyError;import com.android.volley.Request.Method;import com.android.volley.toolbox.StringRequest;import com.android.volley.toolbox.Volley;import com.sprd.Fragment;import com.sprd.easymusic.myview.LrcView;import com.sprd.easymusic.service.MusicService;import com.sprd.easymusic.util.BitmapUtil;import com.sprd.easymusic.util.DownloadUtil;import com.sprd.easymusic.util.LrcLine;import com.sprd.easymusic.util.StringUtil;import android.app.ActionBar;import android.app.Activity;import android.app.Service;import android.content.BroadcastReceiver;import ponentName;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.content.ServiceConnection;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.AsyncTask;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.os.IBinder;import android.os.Message;import android.util.Log;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.View.OnClickListener;import android.view.animation.Animation;import android.view.animation.AnimationUtils;import android.widget.ImageView;import android.widget.ProgressBar;import android.widget.SeekBar;import android.widget.SeekBar.OnSeekBarChangeListener;import android.widget.TextView;import android.widget.Toast;public class PlayMusicActivity extends Activity implements MusicService.Watcher {private static final String TAG = "PlayMusicActivity";/*** 发送音乐播放控制的广播给MianActivity,音乐的控制统一交给MainActivity管理* 下一首,暂停,上一首,播放,继续播放*/private static final String ACTION_NEXT_SONG = "action.nextsong";private static final String ACTION_PAUSE = "action.pause";private static final String ACTION_PRE_SONG = "action.presong";private static final String ACTION_PLAY_SONG = "action.playsong";private static final String ACTION_CONTINUE_PLAYING_SONG = "action.continueplaying";/*** 接收广播,下载歌词完成,下载专辑图片完成,更新播放状态*/private static final String ACTION_DOWNLOADLRC_SUCCESS = "action_downloadlrc_success";private static final String ACTION_DOWNLOADPIC_SUCCESS = "action_downloadpic_success";private static final String ACTION_UPDATE_PLAYSTATE = "action.update.playstate";/*** 上一首,播放/暂停,下一首,专辑图片, 循环*/private ImageView pre, playAndPause, next, albumPic, cycleView;/*** 音乐标题,歌手,时长,已播放时长*/private TextView title, artist, duration, playedTimeView;//加载歌词private TextView loadLrc;//歌词View--自定义Viewprivate LrcView lrcView;//进度条private SeekBar seekBar;//表征当前播放状态private boolean isPlaying;//表征暂停键是否按下,若为true则下次点击播放为继续播放,和isPlaying不冲突private boolean pause;//音乐播放器后台服务private MusicService musicService;private Context mContext;//当前播放音乐的标题和歌手和时长private String currentMusicTitle, currentMusicArtist;private int currentMusicDuration;//播放界面专辑图片的动画-旋转private Animation playAnimation;//当前播放的进度private int currentProgress;private Handler myHandler;private static final int UPDATE_PROGRESS = 1;//添加网络请求队列private RequestQueue mQueue;//搜索歌词的APIpublic static final String lrcApi = "http://geci.me/api/lyric/";//歌词下载的URL和网络响应private String lrcUrl;private String lrcResponse;//缓存专辑图片,避免每次从本地或网络加载private Map<String, SoftReference<Bitmap>> playImageCacheMap = new HashMap<String, SoftReference<Bitmap>>();//当前歌词是否正在展示private boolean showLrc = false;//循环控制切换对应的图片资源private int[] cycleViewResource;protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.play);mContext = this;//绑定服务并获得musicService的引用bindToService();cycleViewResource = new int[] {R.drawable.cycle_list, R.drawable.cycle_single, R.drawable.cycle_random};pre = (ImageView) findViewById(R.id.pre);pre.setOnClickListener(musicClickListener);playAndPause = (ImageView) findViewById(R.id.playAndpause);playAndPause.setOnClickListener(musicClickListener);next = (ImageView) findViewById(R.id.next);next.setOnClickListener(musicClickListener);albumPic = (ImageView) findViewById(R.id.lrcpic);title = (TextView) findViewById(R.id.title);artist = (TextView) findViewById(R.id.artist);duration = (TextView) findViewById(R.id.duration);playedTimeView = (TextView) findViewById(R.id.playedtime);lrcView = (LrcView) findViewById(R.id.lrcview);loadLrc = (TextView) findViewById(R.id.loadlrc);loadLrc.setOnClickListener(musicClickListener);cycleView = (ImageView) findViewById(R.id.cycleview);cycleView.setOnClickListener(musicClickListener);mQueue = Volley.newRequestQueue(mContext);playAnimation = AnimationUtils.loadAnimation(mContext, R.anim.rotate);seekBar = (SeekBar) findViewById(R.id.musicProgress);seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {if (fromUser) {//手动滑动进度条时的监听changeProgressFromUser(progress);//通知后台musicService改变播放进度//歌词信息跳转进度lrcView.checkLrcTime(StringUtil.formatDuration(progress), progress - currentProgress);}}public void onStartTrackingTouch(SeekBar seekBar) {}public void onStopTrackingTouch(SeekBar seekBar) {}});myHandler = new Handler() {public void handleMessage(Message msg) {switch (msg.what) {case UPDATE_PROGRESS://更新播放进度int progress = (Integer)msg.obj;seekBar.setProgress(progress);String playedTime = StringUtil.formatDuration(progress);playedTimeView.setText(playedTime);lrcView.checkLrcTime(playedTime, 1);Log.d(TAG, "update progress success!");break;default:break;}}};IntentFilter filter = new IntentFilter();filter.addAction(ACTION_DOWNLOADLRC_SUCCESS);filter.addAction(ACTION_DOWNLOADPIC_SUCCESS);filter.addAction(ACTION_UPDATE_PLAYSTATE);mContext.registerReceiver(updateReceiver, filter);}// 绑定服务时的ServiceConnection参数private ServiceConnection conn = new ServiceConnection() {// 绑定成功后该方法回调,并获得服务端IBinder的引用public void onServiceConnected(ComponentName name, IBinder service) {// 通过获得的IBinder获取PlayMusicService的引用musicService = ((MusicService.MusicBinder) service).getService();musicService.addWatcher(PlayMusicActivity.this);//Toast.makeText(mContext, "onServiceConnected:musicService", Toast.LENGTH_LONG).show();}@Overridepublic void onServiceDisconnected(ComponentName name) {Log.d(TAG, "onServiceDisconnected:musicService");}};// 绑定服务MusicServiceprivate void bindToService() {bindService(new Intent(mContext,com.sprd.easymusic.service.MusicService.class), conn,Service.BIND_AUTO_CREATE);}//后台播放服务改变播放进度public void changeProgressFromUser(int progress) {musicService.changePlayProgress(progress);}protected void onStart() {updataPlayState();super.onStart();}protected void onDestroy() {musicService = null;this.unbindService(conn);this.unregisterReceiver(updateReceiver);super.onDestroy();}//更新播放状态private void updataPlayState() {Log.d(TAG, "updataPlayState");currentMusicTitle = MainActivity.currentMusicTitle;currentMusicArtist = MainActivity.currentMusicArtist;currentMusicDuration = (int) MainActivity.currentMusicDuration;isPlaying = MainActivity.isPlaying;pause = MainActivity.pause;seekBar.setMax((int)currentMusicDuration);title.setText(currentMusicTitle);artist.setText(currentMusicArtist);duration.setText(StringUtil.formatDuration(currentMusicDuration));if (isPlaying) {playAndPause.setImageResource(android.R.drawable.ic_media_pause);albumPic.startAnimation(playAnimation);} else {playAndPause.setImageResource(android.R.drawable.ic_media_play);albumPic.clearAnimation();}showAlbumPic();showLrc();}//控制音乐的播放转交给MianActivityprivate void changeMusicState(Intent intent) {Log.d(TAG, "changeMusicState:action = " + intent.getAction());this.sendBroadcast(intent);}private OnClickListener musicClickListener = new OnClickListener() {public void onClick(View v) {if (v == pre) {//上一首isPlaying = true;pause = false;showLrc = false;Intent intent = new Intent(ACTION_PRE_SONG);changeMusicState(intent);} else if (v == playAndPause) {//播放或者暂停if (pause) {//如果点击过暂停,下一次点击播放应当是继续播放isPlaying = true;pause = false;Intent intent = new Intent(ACTION_CONTINUE_PLAYING_SONG);changeMusicState(intent);return;}if (isPlaying) {//当前播放状态点击暂停isPlaying = false;pause = true;Intent intent = new Intent(ACTION_PAUSE);changeMusicState(intent);} else {//首次点击播放键,此时isPlaying和pause均为falseisPlaying = true;Intent intent = new Intent(ACTION_PLAY_SONG);changeMusicState(intent);}} else if (v == next) {//下一首isPlaying = true;pause = false;showLrc = false;Intent intent = new Intent(ACTION_NEXT_SONG);changeMusicState(intent);} else if (v == loadLrc) {//加载歌词showLrc = true;String localUrl = getLocalPath(0);File file = new File( localUrl);if (file.exists()) {showLrc();return;}searchLrc(currentMusicTitle, currentMusicArtist);} else if (v == cycleView) {//切换循环模式MainActivity.cycle += 1;if (MainActivity.cycle > 3) MainActivity.cycle = 1;cycleView.setImageResource(cycleViewResource[MainActivity.cycle-1]);}}};//搜索歌词private void searchLrc(String title, String artist) {String searchTitle = null;String searchArtist = null;Log.d(TAG, currentMusicTitle + currentMusicArtist);try {searchTitle = URLEncoder.encode(StringUtil.removeReg(currentMusicTitle, null), "UTF-8");searchArtist = URLEncoder.encode(StringUtil.removeReg(currentMusicArtist, null), "UTF-8");Log.d(TAG, searchTitle + searchArtist);} catch (UnsupportedEncodingException e) {e.printStackTrace();}String url = lrcApi + searchTitle + "/" + searchArtist;if (searchTitle.indexOf("+")>=0 || searchArtist.indexOf("+")>=0) {url = url.replace('+', ' ');}Log.d(TAG, "url = " + url );StringRequest stringRequest = new StringRequest(Method.GET,url, new Response.Listener<String>() { @Override public void onResponse(String response) {Log.d(TAG, "response = " + response); //搜索歌词得到的响应lrcResponse = response;analysisLrcUrl();} }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) {Log.e(TAG, error.getMessage(), error); Toast.makeText(PlayMusicActivity.this, "加载失败!", 300).show();} });mQueue.add(stringRequest); }//搜索专辑图片private void searchAlbumPic(String title, String artist) {Toast.makeText(mContext, "搜索专辑图片...", 300).show();//有些歌名和歌手信息中可能会携带些非原始的特殊符号需要去掉,比如《》.,之类的String ti = StringUtil.removeReg(title, null);String ar = StringUtil.removeReg(artist, null);Log.d(TAG, "searchAlbumPic:ti = " + ti + " ar = " + ar); //启动查询歌词的异步任务new NetFragment().new SearchMusicTask(ti, ar, mContext).execute(NetFragment.getRealUrl(title));}//从响应中分离出歌词下载的URLprivate void analysisLrcUrl() {try {JSONObject jo = new JSONObject(lrcResponse);JSONArray result = jo.getJSONArray("result");JSONObject firstResult = result.getJSONObject(0);String lrc = firstResult.getString("lrc");lrcUrl = lrc;downloadLrc(lrcUrl);Log.d(TAG, "lrc = " + lrc);} catch (JSONException e) {e.printStackTrace();Toast.makeText(PlayMusicActivity.this, "抱歉,未搜索到歌词!", 300).show();}}//下载歌词private void downloadLrc(String lrcUrl) {final String musicLrc = lrcUrl;Toast.makeText(mContext, "正在下载歌词,请稍候...", 300).show();String localFile = getLocalPath(0);File file = new File(localFile);//开启一个线程下载歌词DownloadUtil lrcUtil = new DownloadUtil(musicLrc, localFile, mContext, 1);musicService.downloadMusic(lrcUtil);}//下载专辑图片public void downloadAlbumPic(String albumPicUrl) {if (albumPicUrl == null) {Toast.makeText(mContext, "未搜索到匹配图片", 300).show();albumPic.setImageResource(R.drawable.rotate);return;}final String musicAlbumPic = albumPicUrl;//Toast.makeText(mContext, "正在下载专辑图片,请稍候...", 300).show();String localFile = getLocalPath(1);File file = new File(localFile);//开启两个线程下载专辑图片DownloadUtil albumPicUtil = new DownloadUtil(musicAlbumPic, localFile, mContext, 2);musicService.downloadMusic(albumPicUtil);}//显示歌词protected void showLrc() {if (!showLrc) {lrcView.setLrcList(null);lrcView.setCurrentLrcIndex(0);lrcView.setVisibility(View.GONE);loadLrc.setVisibility(View.VISIBLE);return;}String localFile = getLocalPath(0);File file = new File(localFile);if (!file.exists()) return;//歌词的解析只需传进去一个歌词文件路径,自定义view-lrcView内部已经封装好lrcView.loadLrc(localFile);loadLrc.setVisibility(View.INVISIBLE);}//显示专辑图片private void showAlbumPic() {//首先从缓存中查询是否已加载有该专辑图片if (playImageCacheMap != null) {Log.d(TAG, "playImageCacheMap.size = " + playImageCacheMap.size());SoftReference<Bitmap> sBitmap = playImageCacheMap.get(currentMusicTitle+currentMusicArtist);if (sBitmap != null) {Bitmap bitmap = sBitmap.get();if (bitmap != null) {albumPic.setImageBitmap(bitmap);Log.d(TAG, "get image from ImageCacheMap");return;} else {playImageCacheMap.remove(currentMusicTitle+currentMusicArtist);}}}//如果缓存中还未存入该专辑图片或者已经被回收,从本地加载并添加到缓存final String localFile = getLocalPath(1);File file = new File(localFile);if (file.exists()) {myHandler.post(new Runnable() {public void run() {Bitmap bitmap = BitmapFactory.decodeFile(localFile);//int size = Math.min(lrcPic.getWidth(), lrcPic.getHeight());//Bitmap bitmap = BitmapUtil.getScropBitmap(localFile, size, size);playImageCacheMap.put(currentMusicTitle + currentMusicArtist, new SoftReference<Bitmap>(bitmap));Log.d(TAG, "add bitmap to ImageCacheMap, bytes = " + bitmap.getByteCount());albumPic.setImageBitmap(bitmap);}});return;}//如果缓存和本地均没有专辑图片则从网络中搜索并加载searchAlbumPic(currentMusicTitle, currentMusicArtist);}//获取本地文件路径,type为0表示歌词路径,为1表示专辑图片路径private String getLocalPath(int type) {String result = null;MainActivity.createFileDir();if (type == 0) {result = MainActivity.downloadedPath+ "/lrc/"+ currentMusicTitle + "-" + currentMusicArtist + ".lrc";} else {result = MainActivity.downloadedPath + "/album/"+ currentMusicTitle + "-" + currentMusicArtist + ".jpg";}return result;}@Overridepublic void update(int currentProgress) {this.currentProgress = currentProgress;myHandler.sendMessage(myHandler.obtainMessage(UPDATE_PROGRESS, currentProgress));}private BroadcastReceiver updateReceiver = new BroadcastReceiver() {public void onReceive(Context context, Intent intent) {if (intent.getAction().equals(ACTION_DOWNLOADLRC_SUCCESS)) {Toast.makeText(mContext, "下载歌词成功!", 100).show();showLrc();} else if (intent.getAction().equals(ACTION_DOWNLOADPIC_SUCCESS)) {Toast.makeText(mContext, "下载专辑图片成功!", 100).show();showAlbumPic();} else if (intent.getAction().equals(ACTION_UPDATE_PLAYSTATE)) {boolean autoChange = intent.getBooleanExtra("autoChange", false);if (autoChange) showLrc = !autoChange;updataPlayState();}}};}

其中StringUtil是字符串处理工具类:

package com.sprd.easymusic.util;import android.media.MediaPlayer;public class StringUtil {// 将音乐时长-毫秒转换为00:00格式public static String formatDuration(long dur) {long totalSecond = dur / 1000;String minute = totalSecond / 60 + "";if (minute.length() < 2)minute = "0" + minute;String second = totalSecond % 60 + "";if (second.length() < 2)second = "0" + second;return minute + ":" + second;}public static String removeReg(String source, String reg) {if (reg!= null) {return source.replaceAll(reg, "");}//保留中文和英文字符return source.replaceAll("[^a-zA-Z \u4e00-\u9fa5]", "");}public static String getMusicDuration(String url) {int duration = 0;MediaPlayer mp = null;try {mp = new MediaPlayer();mp.reset();mp.setDataSource(url);mp.prepare();duration = mp.getDuration();} catch (Exception e) {e.printStackTrace();} finally {mp.release();}return formatDuration(duration);}public static long getMusicLongDuration(String url) {long duration = 0;MediaPlayer mp = null;try {mp = new MediaPlayer();mp.reset();mp.setDataSource(url);mp.prepare();duration = mp.getDuration();} catch (Exception e) {e.printStackTrace();} finally {mp.release();}return duration;}}

搜索专辑图片的异步任务与网络音乐搜索的异步任务是同一个,在前面的章节中可以看到搜索歌曲时有保存专辑图片,只是没有拿来使用。

PlayMusicActivity.java的布局很简单,如下:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"android:id="@+id/playlayout"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/myshape"android:gravity="top"android:orientation="vertical"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin" ><LinearLayout android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="2"android:gravity="center"android:orientation="vertical" ><TextView android:id="@+id/title"android:layout_width="wrap_content"android:layout_height="30dp"android:textAppearance="?android:attr/textAppearanceLarge"android:textColor="#ff0000" /><TextView android:id="@+id/artist"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textAppearance="?android:attr/textAppearanceMedium"android:textColor="#00ff00" /></LinearLayout><RelativeLayout android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="20" ><!--<ImageViewandroid:id="@+id/lrcpic"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="20dp"android:src="@drawable/rotate" /> --><com.sprd.easymusic.myview.CircleImageView android:id="@+id/lrcpic"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="20dp"android:alpha="0.6"android:src="@drawable/rotate"/><com.sprd.easymusic.myview.LrcView android:id="@+id/lrcview"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="20dp"android:alpha="1.0"android:visibility="gone"/></RelativeLayout><TextView android:id="@+id/loadlrc"android:layout_width="match_parent"android:layout_height="0dp"android:layout_gravity="center"android:layout_marginBottom="10dp"android:layout_weight="4"android:gravity="center"android:text="查看歌词"android:textAppearance="?android:attr/textAppearanceMedium"android:textColor="#ff00ff" /><SeekBar android:id="@+id/musicProgress"android:layout_width="match_parent"android:layout_height="30dp"android:layout_marginBottom="10dp" /><LinearLayout android:id="@+id/seekbar"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="10dp"android:orientation="horizontal" ><TextView android:id="@+id/playedtime"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="5dp"android:layout_weight="4"android:text="00:00"android:textColor="#ffffff" /><TextView android:id="@+id/duration"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_weight="4"android:gravity="right"android:textColor="#ffffff" /></LinearLayout><LinearLayout android:id="@+id/controlarea"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="30dp"android:layout_weight="5"android:gravity="center"android:orientation="horizontal" ><ImageView android:id="@+id/pre"android:layout_width="30dp"android:layout_height="30dp"android:layout_marginLeft="30dp"android:src="@drawable/pre" /><ImageView android:id="@+id/playAndpause"android:layout_width="30dp"android:layout_height="30dp"android:layout_marginLeft="50dp"android:background="#ffffff"android:src="@drawable/play" /><ImageView android:id="@+id/next"android:layout_width="30dp"android:layout_height="30dp"android:layout_marginLeft="50dp"android:src="@drawable/next" /><ImageView android:id="@+id/cycleview"android:layout_width="30dp"android:layout_height="30dp"android:layout_marginLeft="50dp"android:src="@drawable/cycle_list" /></LinearLayout></LinearLayout>

这是一个线性布局,从各个view的id就可以看出其作用了,这里也不多做解释了。

大概界面预览图如下(专辑图片eclipse显示有误):

音乐播放器已完成,下载地址:

Android音乐播放器

如果觉得《基于android的网络音乐播放器-播放控制界面(九)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。