Android开发-仿网易云音乐播放器样式设计与实现
前 言
大家平时在听音乐时使用到的网易云音乐 Android 版 App 时有没有发现网易云音乐的 App 样式做的比较好,App 抽屉式菜单栏使用 Android 独有的特性(相对于IOS) Material Design 风格的设计模式,App 整体风格设计样式符合人性设计。那么这篇博客主要讲如何实现仿网易云音乐简易版播放器。
需求分析
要实现仿网易云音乐简易版播放器的功能,需要实现以下几个功能和步骤:
- 自定义音乐播放器 View 的样式;
- 实现播放和暂停音乐时指针和光盘的动画效果;
- 实现音乐后台播放的服务。
自定义音乐播放器 View 的样式以及指针和光盘的动画效果
创建一个 xml 布局 play_music.xml 文件,布局样式如下:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="wrap_content"android:layout_height="wrap_content"><!-- 光盘 --><FrameLayoutandroid:id="@+id/fl_play_music"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="90dp"><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/disc" /><!-- CircleImageView --><de.hdodenhof.circleimageview.CircleImageViewandroid:id="@+id/iv_icon"android:layout_width="210dp"android:layout_height="210dp"android:layout_gravity="center"app:civ_border_color="@android:color/black"app:civ_border_width="2dp" /><ImageViewandroid:id="@+id/iv_play"android:layout_width="60dp"android:layout_height="60dp"android:layout_gravity="center"android:src="@drawable/play_music"android:visibility="gone" /></FrameLayout><!-- 指针 --><ImageViewandroid:id="@+id/iv_needle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:layout_marginStart="25dp"android:src="@drawable/needle" /></FrameLayout>
布局主要包含播放音乐的光盘和指针以及光盘内音乐封面和播放按钮。
创建自定义 View 文件 PlayMusicView.java,逻辑代码如下:
public class PlayMusicView extends FrameLayout {private Context mContext;private MusicModel mMusicModel;private MusicService.MusicBind mMusicBinder;private Intent mServiceIntent;private boolean isPlaying, isBindService;private View mView;private FrameLayout mFlPlayMusic;private ImageView mIvIcon, mIvNeedle, mIvPlay;private Animation mPlayMusicAnim, mPlayNeedleAnim, mStopNeedleAnim;public PlayMusicView(@NonNull Context context) {super(context);init(context);}public PlayMusicView(@NonNull Context context, @Nullable AttributeSet attrs) {super(context, attrs);init(context);}public PlayMusicView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context);}@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)public PlayMusicView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);init(context);}private void init(Context context) {// MediaPlayermContext = context;mView = LayoutInflater.from(mContext).inflate(R.layout.play_music, this, false);mFlPlayMusic = mView.findViewById(R.id.fl_play_music);mFlPlayMusic.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {trigger();}});mIvIcon = mView.findViewById(R.id.iv_icon);mIvNeedle = mView.findViewById(R.id.iv_needle);mIvPlay = mView.findViewById(R.id.iv_play);/*** 1、定义所需要执行的动画* 1、光盘转动的动画* 2、指针指向光盘的动画* 3、指针离开光盘的动画* 2、startAnimation*/mPlayMusicAnim = AnimationUtils.loadAnimation(mContext, R.anim.play_music_anim);mPlayNeedleAnim = AnimationUtils.loadAnimation(mContext, R.anim.play_needle_anim);mStopNeedleAnim = AnimationUtils.loadAnimation(mContext, R.anim.stop_needle_anim);addView(mView);}/*** 切换播放状态*/private void trigger() {if (isPlaying) {stopMusic();} else {playMusic();}}/*** 播放音乐*/public void playMusic() {isPlaying = true;mIvPlay.setVisibility(View.GONE);mFlPlayMusic.startAnimation(mPlayMusicAnim);mIvNeedle.startAnimation(mPlayNeedleAnim);// 启动服务startMusicService();}/*** 停止播放*/public void stopMusic() {isPlaying = false;mIvPlay.setVisibility(View.VISIBLE);mFlPlayMusic.clearAnimation();mIvNeedle.startAnimation(mStopNeedleAnim);mMusicBinder.stopMusic();}/*** 设置光盘中显示的音乐封面图片*/private void setMusicIcon() {Glide.with(mContext).load(mMusicModel.getPoster()).into(mIvIcon);}/*** 设置音乐播放模型*/public void setMusic(MusicModel musicModel) {this.mMusicModel = musicModel;setMusicIcon();}/*** 启动音乐服务*/private void startMusicService() {if (mServiceIntent == null) {mServiceIntent = new Intent(mContext, MusicService.class);mContext.startService(mServiceIntent);} else {mMusicBinder.playMusic();}// 当前未绑定,绑定服务,同时修改绑定状态if (!isBindService) {isBindService = true;mContext.bindService(mServiceIntent, conn, Context.BIND_AUTO_CREATE);}}/*** 销毁方法,需要在 activity 被销毁的时候调用*/public void destroy() {// 如果已绑定服务,则解除绑定,同时修改绑定状态if (isBindService) {isBindService = false;mContext.unbindService(conn);}}ServiceConnection conn = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mMusicBinder = (MusicService.MusicBind) service;mMusicBinder.setMusic(mMusicModel);mMusicBinder.playMusic();}@Overridepublic void onServiceDisconnected(ComponentName name) {}};
}
该逻辑代码主要实现了音乐播放时所要执行的动画效果,比如:光盘转动的动画、指针指向光盘的动画以及指针离开光盘的动画。还有播放和暂停音乐时切换音乐的播放状态样式和后台服务等。
然后在创建一个用来显示播放器界面的 PlayMusicActivity.java 文件,逻辑代码如下:
public class PlayMusicActivity extends AppCompatActivity {public static final String NAME = "name";public static final String POSTER = "poster";public static final String PATH = "path";public static final String AUTHOR = "author";private String mName;private String mPoster;private String mPath;private String mAuthor;private MusicModel mMusicModel;@BindView(R.id.iv_bg)ImageView mIvBg;@BindView(R.id.tv_name)TextView mTvName;@BindView(R.id.tv_author)TextView mTvAuthor;@BindView(R.id.play_music_view)PlayMusicView mPlayMusicView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_play_music);// 隐藏状态栏getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);initView();}private void initView() {ButterKnife.bind(this);mName = getIntent().getStringExtra(NAME);mPoster = getIntent().getStringExtra(POSTER);mPath = getIntent().getStringExtra(PATH);mAuthor = getIntent().getStringExtra(AUTHOR);mMusicModel = new MusicModel();mMusicModel.setName(mName);mMusicModel.setPath(mPath);mMusicModel.setPoster(mPoster);mMusicModel.setAuthor(mAuthor);Glide.with(this).load(mMusicModel.getPoster())// 设置音乐播放器背景图片的高斯模糊度.apply(RequestOptions.bitmapTransform(new BlurTransformation(25, 35))).into(mIvBg);mTvName.setText(mMusicModel.getName());mTvAuthor.setText(mMusicModel.getAuthor());mPlayMusicView.setMusic(mMusicModel);mPlayMusicView.playMusic();}/*** 后退按钮点击事件*/public void onBackClick(View view) {onBackPressed();}@Overrideprotected void onDestroy() {super.onDestroy();mPlayMusicView.destroy();}}
其中的代码 apply(RequestOptions.bitmapTransform(new BlurTransformation(25, 35))) 是使用图片加载框架 Glide 和 glide-transformations 来实现音乐播放器背景图片的高斯模糊效果,其中 BlurTransformation 第一个参数是采集图片的半径,第二个参数是采集率。
实现音乐后台播放的服务
当音乐播放时我们要把音乐播放的操作放在后台服务,我们创建一个音乐服务文件 MusicService.java ,逻辑代码如下:
/*** 1、通过Service 连接 PlayMusicView 和 MediaPlayHelper* 2、PlayMusicView -- Service:* 1、播放音乐、暂停音乐* 2、启动Service、绑定Service、解除绑定Service* 3、MediaPlayHelper -- Service:* 1、播放音乐、暂停音乐* 2、监听音乐播放完成,停止 Service*/
public class MusicService extends Service {// 不可为 0public static final int NOTIFICATION_ID = 1;private MediaPlayerHelp mMediaPlayerHelp;private MusicModel mMusicModel;public MusicService() {}public class MusicBind extends Binder {/*** 设置音乐(MusicModel)*/public void setMusic(MusicModel musicModel) {mMusicModel = musicModel;startForeground();}/*** 播放音乐*/public void playMusic() {/*** 1、判断当前音乐是否是已经在播放的音乐* 2、如果当前的音乐是已经在播放的音乐的话,那么就直接执行start方法* 3、如果当前播放的音乐不是需要播放的音乐的话,那么就调用setPath的方法*/if (mMediaPlayerHelp.getPath() != null&& mMediaPlayerHelp.getPath().equals(mMusicModel.getPath())) {mMediaPlayerHelp.start();} else {mMediaPlayerHelp.setPath(mMusicModel.getPath());mMediaPlayerHelp.setOnMeidaPlayerHelperListener(new MediaPlayerHelp.OnMeidaPlayerHelperListener() {@Overridepublic void onPrepared(MediaPlayer mp) {mMediaPlayerHelp.start();}@Overridepublic void onCompletion(MediaPlayer mp) {stopSelf();}});}}/*** 暂停播放*/public void stopMusic() {mMediaPlayerHelp.pause();}}@Overridepublic IBinder onBind(Intent intent) {return new MusicBind();}@Overridepublic void onCreate() {super.onCreate();mMediaPlayerHelp = MediaPlayerHelp.getInstance(this);}/*** 系统默认不允许不可见的后台服务播放音乐,* Notification ,*//*** 设置服务在前台可见*/private void startForeground() {/*** 通知栏点击跳转的intent*/PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,MainActivity.class), PendingIntent.FLAG_CANCEL_CURRENT);/*** 创建Notification*/Notification notification = null;/*** android API 26 以上 NotificationChannel 特性适配*/if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {NotificationChannel channel = createNotificationChannel();notification = new Notification.Builder(this, channel.getId()).setContentTitle(mMusicModel.getName()).setContentText(mMusicModel.getAuthor()).setSmallIcon(R.mipmap.ic_launcher).setContentIntent(pendingIntent).build();NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);notificationManager.createNotificationChannel(channel);} else {notification = new Notification.Builder(this).setContentTitle(mMusicModel.getName()).setContentText(mMusicModel.getAuthor()).setSmallIcon(R.mipmap.ic_launcher).setContentIntent(pendingIntent).build();}/*** 设置 notification 在前台展示*/startForeground(NOTIFICATION_ID, notification);}@RequiresApi(api = Build.VERSION_CODES.O)private NotificationChannel createNotificationChannel() {String channelId = "CloudMusic";String channelName = "CloudMusicTestService";String Description = "CloudMusicTest";NotificationChannel channel = new NotificationChannel(channelId,channelName, NotificationManager.IMPORTANCE_HIGH);channel.setDescription(Description);channel.enableLights(true);channel.setLightColor(Color.RED);channel.enableVibration(true);channel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});channel.setShowBadge(false);return channel;}
}
该逻辑代码的主要功能实现了启动音乐 Service 和解绑音乐 Service 的操作以及音乐播放时把音乐播放的状态显示到通知栏里。当开始播放音乐时,如果音乐 Service 进程已经被解绑或者“杀死”掉则重新开启音乐 Service ,如果当前有其它音乐正在播放,那么先结束上一个的音乐播放再进行当前播放当前的音乐等。
界面运行效果图如下:
apk安装包下载体验地址:
可以扫描以下二维码进行下载安装,或者点击以下链接 http://app.fukaimei.top/CloudMusicTest 进行下载安装体验。
———————— The end ————————
码字不易,如果您觉得这篇博客写的比较好的话,可以赞赏一杯咖啡吧~~
Demo程序源码下载地址一(GitHub)
Demo程序源码下载地址二(码云)
Android开发-仿网易云音乐播放器样式设计与实现相关推荐
- 移动应用开发——uni-app框架 仿网易云音乐播放器学习心得
目录 一.uni-app框架介绍 1.什么是 uni-app 2.为什么要选择uni-app 3.uni-app 统一规范 4.uni-app功能框架 二.开发工具与项目创建 1.开发工具 2.项目创 ...
- Vue仿网易云音乐播放器(一)
项目简介 写了很多关于Vue的项目,都是一部分一部分的小模块,这次想把全部学过的关于Vue知识和模块写成一个完整的项目.都是组件化进行mvvm模式开发,实现了view和data的同步更新.仿网易云播放 ...
- uniapp 仿网易云音乐播放器 微信小程序
效果视频: uniapp 仿照网易云播放器功能 效果截图: 上代码: <template><view class=""><scroll-view :s ...
- Android高仿网易云音乐播放界面
现在很多的播放器的播放界面都是采用光盘的转动,下面是我仿造网易的播放界面.先上两张图: 第一张为播放前的界面,第二张为点击播放按钮的图片.布局文件如下: <RelativeLayout xmln ...
- Vue仿网易云音乐播放器(二)
项目运行 首先要安装npm或者cnpm和node.js环境 在终端建立vue-cli项目,命令行cnpm install vue-cli -g //全局安装 vue-cli 查看vue-cli是否成功 ...
- HTML+CSS+JAVASCRIPT 高仿低配网页版网易云音乐播放器 1
HTML+CSS+JAVASCRIPT 高仿低配网页版网易云音乐播放器 前言 没有使用任何框架,只是想用最简单纯js的代码实现下 前台: Javascript+jQuery 后台: php/nodej ...
- Android仿网易云音乐播放页面 背景虚化碟片效果
1.效果图 仿网易云音乐播放页面,主要有4个关键点: 背景虚化.获取音乐的专辑封面,将此图片作为背景图,并进行模糊虚化处理 碟片合成.获取音乐的专辑封面,和黑色碟片图片进行合成 碟片旋转.音乐播放时, ...
- 第三方网易云音乐播放器ieaseMusic Mac中文版v1.3.4版
ieasemusic mac中文版是目前mac平台上最好的网易云音乐播放器,与网易云音乐官方客户端最大的区别是,官方的就是「客户端」,完全按照其产品内容进行直接堆砌布局,而 ieaseMusic 是基 ...
- 树莓派云音乐c语言,基于树莓派的红外遥控版网易云音乐播放器
基于树莓派的红外遥控版网易云音乐播放器.下面是遥控键盘示意图: CH- CH CH+ << >> || - + EQ 0 100+ 200+ 1 2 3 4 5 6 7 8 9 ...
- linux树莓派网易云音乐,基于树莓派的红外遥控版网易云音乐播放器
基于树莓派的红外遥控版网易云音乐播放器.下面是遥控键盘示意图: CH- CH CH+ << >> || - + EQ 0 100+ 200+ 1 2 3 4 5 6 7 8 9 ...
最新文章
- spring boot+logbak项目打成jar包使用java -jar命令启动后日志出现中文乱码
- 媒体查询media的3种引入方式
- Java设计模式笔记(7)适配器模式
- android webview 水平滚动,Android WebView不可滚动
- 【Spring AOP】基于注解的 AOP 编程
- 为什么要对1000000007取模
- php 无刷新上传,php 无刷新上传文件的代码
- cocos2dx 制作单机麻将(四)
- 三角函数到傅立叶级数
- tplink 文件服务器,tplink云服务器
- Docer安装postgreSQL和pgadin4
- dve 二维数组信号 显示波形_交互式仿真下dve和verdi中查看二维数组值
- 分析各渠道广告,建立评分模型以及找到优质渠道的特性
- 哈尔滨工程大学学生郭天祥六年非一般的大学
- Android 底部导航栏 BottomNavigationBar
- kolla 部署openstack train版本(三节点controller+compute+storage)------功能测试
- python里的map是什么意思_python中的map是什么意思
- selenium登录 爬取淘宝商品信息
- matlab画动图留下末端点轨迹,请问Matlab robotic Toolbox中怎么让机械手运动时末端走过的轨迹显示出来?...
- Waymo二次裁员/特斯拉召回,自动驾驶赛道「新」变化