一、音乐播放器概述与分析
目前手机的音乐播放功能已经是大家比较关注的一个部分,不少在人在购买手机的时候都会关心手机的音乐播放的能力,这也足以看出目前大家对音乐播放功能的重视,所以一款性能良好的手机音乐播放器软件一定会受到欢迎。和传统的音乐播放器相比,手机播放音乐更加的容易携带,其他方面也都不输于传统音乐播放器,而且还具有网上听音乐功能,所以开发一款受欢迎的手机音乐播放软件是具有良好的市场和应用前景的。不同手机平台的手机,所支持的音乐播放软件也是各不相同,而现在最流行的手机平台之一就是google 的android手机平台,所以本课题就致力于开发出一款基于android手机平台的音乐播放器,来满足用户的使用需求。
本次Android课设音乐播放器完成的基本功能有: (1)主界面音乐播放管理:实现音乐的播放、暂停、播放进度条显示及拉动、播放上一首、播放下一首、播放模式的列表循环、单曲循环、随机播放等基本功能。 (2)通知栏音乐播放管理:使用Service、BroadcastReceiver可以在状态通知栏实现音乐播放的基本功能、打开和退出等功能。 (3)桌面小组件音乐播放管理:使用Service、BroadcastReceiver、重写AppWidgetProvider类在桌面以小组件的方式实现音乐播放基本功能。 (4)音乐搜索管理:获取QQ音乐的音乐数据,实现音乐搜索,保存并显示搜索记录,可对历史搜索记录进行删除和清空。 (5)歌单管理:获取QQ音乐的音乐数据的榜单信息,实现榜单歌单,可添加歌曲到我喜欢和播放列表等。
二、音乐播放器的设计
针对音乐播放器系统的功能进行详细分析,整个应用程序应划分为3个模块,分别是程序启动、用户界面和数据库适配器。
程序启动时需要连接网络访问数据,获取数据并更新显示到主界面榜单列表页面中,具体如下图所示: 用户界面模块包括榜单列表的主界面、用户界面、播放界面、搜索界面、播放界面、歌单界面,具体如下图所示: 数据库模块数据库适配器主要对存入数据库的专辑图片、专辑名称、歌手、歌名、音乐、付费情况数据进行管理,包括添加、删除、查询相关数据,具体如下图所示: 用户进入音乐播放器后,会看到榜单列表的主界面。在主界面上进行的各种操作,截图如下流程图所示: (1)在主界面(榜单列表)时,可以选择点击播放列表,查看播放列表中的歌曲,如有想要播放的音乐,点击歌曲进入播放,否则可以选择关闭按钮关闭播放列表回到原来的界面,也可以通过歌曲栏中的播放按钮播放当前歌曲。
(2)在主界面(榜单列表)中点击歌曲栏,可以进入播放界面,进行播放音乐的操作,如播放、暂停、上一首、下一首,以及更改播放模式,或者返回主界面。
(3)在主界面(榜单列表)中点击“我的”可以进入用户界面,在用户界面中可以点击“喜欢”查看喜欢列表的歌曲,点击“最近”查看最近播放列表的歌单,或者返回主界面。
(4)在主界面(榜单列表)中点击榜单列表中的榜单(包括我喜欢列表),可以查看榜单中的歌曲,如有想要播放的歌曲可以点击歌曲播放,或者返回主界面。
(5)在主界面(榜单列表)中点击“搜索”,可以在编辑栏输入想要搜索的歌手、歌名、专辑等,查询后出现相关歌单列表,选择可以播放歌曲。同时,会显示搜索历史,点击可以继续搜索以前的记录,或删除历史记录,或清空所有历史,否则选择返回主界面。
(6)在播放界面中,滑动可查看歌词,拖动进度条可拖动播放,点击暂停或播放、上一首、下一首控制播放,点击“模式”可实现随机播放、顺序播放、单曲循环,也可查看播放列表。点击“操作”可以添加我喜欢声音控制,或者选择关闭。
三、音乐播放器的实现
1、功能实现 1.1 获取QQ音乐数据
public static String getMusicApi(int topid) {
return "https://c.y.qq.com/v8/fcg-bin/fcg_v8_toplist_cp.fcg?g_tk=5381&uin=0&format=json&inCharset=utf-8&outCharset=utf-8?ice=0&platform=h5&needNewCode=1&tpl=3&page=detail&type=top&topid=" + topid;
}
public static String getAlbumImg(String albummid) {
return "https://y.gtimg.cn/music/photo_new/T002R300x300M000" + albummid + ".jpg";
}
public static final String musicUrl = "http://ws.stream.qqmusic.qq.com/";
public static String getMusicKey(String songmid) {
return "https://u.y.qq.com/cgi-bin/musicu.fcg?format=json&data=" +
"{\"req_0\":{" +
"\"module\":\"vkey.GetVkeyServer\"," +
"\"method\":\"CgiGetVkey\"," +
"\"param\":{" +
"\"guid\":\"1\"," +
"\"songmid\":[\"" + songmid + "\"]," +
"\"uin\":\"1\"" +
"}}}";
}
public static final String qqmusicHeadName = "Referer";
public static final String qqmusicHeadValue = "https://y.qq.com/portal/player.html";
public static String getLyric(String songmid) {
return "https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?format=json&nobase64=1&songmid=" + songmid;
}
public static String getSearch(String name) {
return "https://c.y.qq.com/soso/fcgi-bin/client_search_cp?p=1&n=20&format=json&w=" + name;
}
1.2 搜索功能
private void searchMusic() {
musicList.clear();
musicAdapter.notifyDataSetChanged();
mPresenter.getData(toolbar.getText());
dbHelper.insertData(toolbar.getText());
historyAdapter.setList(dbHelper.queryData());
layoutHistory.setVisibility(View.GONE);
rvMusic.setVisibility(View.VISIBLE);
}
1.3 桌面组件
@Override
public void onEnabled(Context context) {
super.onEnabled(context);
Log.d(TAG, "onEnabled: ");
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
Log.d(TAG, "onUpdate: ");
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.music_widget);
views.setImageViewResource(R.id.mw_albumimg, R.drawable.pic_launcher);
views.setTextViewText(R.id.mw_text, "选择一首音乐");
views.setProgressBar(R.id.mw_progress, 100, 0, false);
views.setOnClickPendingIntent(R.id.mw_layout, startActivity(context));
views.setOnClickPendingIntent(R.id.mw_play, sendBrocast(context, ACTION_WIDGET_PLAY));
views.setOnClickPendingIntent(R.id.mw_previous, sendBrocast(context, ACTION_WIDGET_PREVIOUS));
views.setOnClickPendingIntent(R.id.mw_next, sendBrocast(context, ACTION_WIDGET_NEXT));
appWidgetManager.updateAppWidget(appWidgetIds, views);
}
@Override
public void onDisabled(Context context) {
super.onDisabled(context);
Log.d(TAG, "onDisabled: ");
}
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
Log.d(TAG, "onReceive: " + intent.getAction());
if (MusicPlayerUtils.isMusicPlayer()) {
MyMusicPlayerView mPlayerView = (MyMusicPlayerView) XXPlayerManager.instance().getCurrentPlayer();
switch (intent.getAction()) {
case ACTION_WIDGET_UPDATE:
updatePlayer(context, mPlayerView);
break;
case ACTION_WIDGET_PLAY:
if (mPlayerView.isIdle()) {
mPlayerView.start();
} else if (mPlayerView.isPlaying() || mPlayerView.isBufferingPlaying()) {
mPlayerView.pause();
} else if (mPlayerView.isPaused() || mPlayerView.isBufferingPaused() ||
mPlayerView.isCompleted() || mPlayerView.isError()) {
mPlayerView.restart();
}
break;
case ACTION_WIDGET_PREVIOUS:
mPlayerView.playNext(false);
break;
case ACTION_WIDGET_NEXT:
mPlayerView.playNext(true);
break;
}
} else {
switch (intent.getAction()) {
case ACTION_WIDGET_PLAY:
case ACTION_WIDGET_PREVIOUS:
case ACTION_WIDGET_NEXT:
Toast.makeText(context, "选择一首音乐", Toast.LENGTH_SHORT).show();
break;
}
}
}
1.4 通知栏
private void updateViews(RemoteViews views) {
Log.d(TAG, "updateViews: " + isPlaying);
if (isPlaying) {
notification.contentView = views;
notificationManager.notify(NOTIFICATION_ID, notification);
Intent intent = new Intent(this, MusicWidgetProvider.class);
intent.setAction(MusicWidgetProvider.ACTION_WIDGET_UPDATE);
sendBroadcast(intent);
}
}
private Notification createNotification() {
Notification.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_HIGH);
channel.enableLights(false);
channel.enableVibration(false);
channel.setVibrationPattern(new long[]{0});
channel.setSound(null, null);
notificationManager.createNotificationChannel(channel);
builder = new Notification.Builder(this, channel.getId());
} else {
builder = new Notification.Builder(this);
}
Notification notification = builder
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(getResources().getString(R.string.app_name))
.setContentIntent(startActivity())
.build();
return notification;
}
1.5 播放栏的暂停、播放、切歌、播放模式
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv_music:
if (TextUtils.isEmpty(mPlayerView.getUrl())) {
((BaseActivity) getContext()).showToast("选择一首音乐");
} else {
enterFullScreen();
}
break;
case R.id.iv_musiclist:
if (TextUtils.isEmpty(mPlayerView.getUrl())) {
((BaseActivity) getContext()).showToast("选择一首音乐");
} else {
showListDialog();
}
break;
case R.id.ppv_music:
if (TextUtils.isEmpty(mPlayerView.getUrl())) {
((BaseActivity) getContext()).showToast("选择一首音乐");
break;
}
if (mPlayerView.isIdle()) {
mPlayerView.start();
ppv_music.play();
} else if (mPlayerView.isPlaying() || mPlayerView.isBufferingPlaying()) {
mPlayerView.pause();
ppv_music.pause();
} else if (mPlayerView.isPaused() || mPlayerView.isBufferingPaused() ||
mPlayerView.isCompleted() || mPlayerView.isError()) {
mPlayerView.restart();
ppv_music.play();
}
break;
case R.id.ppv_play:
if (mPlayerView.isIdle()) {
mPlayerView.start();
ppv_play.play();
} else if (mPlayerView.isPlaying() || mPlayerView.isBufferingPlaying()) {
mPlayerView.pause();
ppv_play.pause();
} else if (mPlayerView.isPaused() || mPlayerView.isBufferingPaused() ||
mPlayerView.isCompleted() || mPlayerView.isError()) {
mPlayerView.restart();
ppv_play.play();
}
break;
case R.id.iv_mode:
if (mPlayerView.getPlayMode() == BasePlayerView.PLAY_MODE_LIST_LOOP) {
iv_mode.setImageResource(R.drawable.ic_singleloop);
mPlayerView.setPlayMode(BasePlayerView.PLAY_MODE_LOOP);
ShareUtils.setMusicMode(getContext(), BasePlayerView.PLAY_MODE_LOOP);
((BaseActivity) getContext()).showToast("切换到单曲循环");
} else if (mPlayerView.getPlayMode() == BasePlayerView.PLAY_MODE_LOOP) {
iv_mode.setImageResource(R.drawable.ic_random);
mPlayerView.setPlayMode(BasePlayerView.PLAY_MODE_RANDOM);
ShareUtils.setMusicMode(getContext(), BasePlayerView.PLAY_MODE_RANDOM);
((BaseActivity) getContext()).showToast("切换到随机播放");
} else if (mPlayerView.getPlayMode() == BasePlayerView.PLAY_MODE_RANDOM) {
iv_mode.setImageResource(R.drawable.ic_listloop);
mPlayerView.setPlayMode(BasePlayerView.PLAY_MODE_LIST_LOOP);
ShareUtils.setMusicMode(getContext(), BasePlayerView.PLAY_MODE_LIST_LOOP);
((BaseActivity) getContext()).showToast("切换到顺序播放");
}
break;
case R.id.iv_list:
showListDialog();
break;
case R.id.iv_previous:
mPlayerView.playNext(false);
break;
case R.id.iv_next:
mPlayerView.playNext(true);
break;
}
}
2、界面实现
(1)榜单列表主界面由SimpleToolbar,SlidingTabLayout,ViewPager,MusicView四种组件组成,界面如下图所示: (2)用户界面主要有RecyclerView,AppBarLayout,Toolbar,SimpleToolbar,CircleImageView,ImageView六种组件组成,界面如下图所示: (3)搜索界面主要有SearchToolbar,TextView,RecyclerView,FrameLayout,RecyclerView几种组件,界面如下图所示: (4)播放界面主要控件ImageView,SimpleToolbar,ViewPager,TextView,SeekBar等组件组成,界面如下图所示: (5)播放列表的界面如下图所示: (6)音乐菜单的界面如下图所示: 四、音乐播放器的演示
1、滑动或点击榜单实现查看不同的榜单中的榜单列表,产生下图变化。点击其中的榜单,以点击“我喜欢”为例,查看其中歌曲信息。
2、在主界面中点击“我的”进入我的界面,,点击“喜欢”后的列表,“最近播放”的列表分别如下图所示。
3、在主页面中点击“搜索”进入搜索页面,搜索“周深”出现相关的歌曲信息,输入为空时,出现提示。删除一项历史记录,清空历史记录分别如下图所示。 4、在主页或其他界面点击播放栏的操作,点击“播放”按钮可以播放当前音乐,点击“播放列表”会显示播放列表信息。点击整个播放栏,进入播放界面,点击播放、暂停、下一首、上一首按钮可实现播放控制。点击播放模式分别弹出顺序播放、循环播放、单曲循环,以随机播放为例。点击播放列表还是如前面一样,滑动屏幕可以实现歌词界面,点击操作栏,可将当前播放歌曲添加到“我喜欢”列表,同时支持搜索歌手和专辑信息、音量控制,分别如下图所示。 5、桌面小组件和状态通知栏的显示分别如下图所示。
需要源码的小伙伴们可以在后台私信我。
|