600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > 【Android】_MediaServer_仿网易云音乐播放器1(指针和唱片)

【Android】_MediaServer_仿网易云音乐播放器1(指针和唱片)

时间:2023-12-17 23:29:27

相关推荐

【Android】_MediaServer_仿网易云音乐播放器1(指针和唱片)

目录

一、MyServer的用法二、仿网易云音乐播放器设计思路(一)网易云播放器构成分析(二)MusicDemo实现功能a. MediaServer播放音乐?b. Seekbar滑动条及时间进度c. 唱针旋转和碟片旋转d.隐藏上边框三、具体编码附:本次项目源码

一、MyServer的用法

(一)原理

Service

是android四大组件之一,主要用于在后台处理一些耗时的逻辑,或者去执行某些需要长期运行的任务。必要的时候我们甚至可以在程序退出的情况下,让Service在后台继续保持运行状态。而Service是运行在主线程里的,如果直接在Service中处理一些耗时的逻辑,就会导致程序ANR,所以需要另外开启子线程来处理。

两种模式:startService()/bindService()

基本用法:

新建MyService继承自Service;

不要忘了在AndroidManifest.xml中注册

MyService Service启动后如果没有StopService操作即使activity被销毁任然会在后台运行

(二)用法

方法一:在java包下,右键选择Server新建MediaServer,这样在AndroidManifest下会自动生成服务;

方法二:自定义MediaServer.java类,继承Server,然后在AndroidManifest里添加:

.....略</activity><serviceandroid:name=".MediaService"android:enabled="true"android:exported="true" /></application>略.....

二、仿网易云音乐播放器设计思路

(一)网易云播放器构成分析

主要包含:

1.播放组键功能,2.歌曲进度条功能, 3.唱片特效功能,4.隐藏上边框

原网易云效果图和我的仿网易云音乐播放器Demo中的对比图:

(二)MusicDemo实现功能

a. MediaServer播放音乐?

main.java部分函数截图:

定义及声明:

的点击事件

b. Seekbar滑动条及时间进度

绑定Server的前提下,run()函数定义seekbar的滚动及test时间值,调用方法放在对button的监听Onclick函数中;

seekbar及textSwitcher的定义:

Oncreate函数中的相关部分声明:

自定义run()函数,控制播放到当前音乐的滑动条时间设置:

c. 唱针旋转和碟片旋转

d.隐藏上边框

自定义styles,在main函数调用

main函数中:

//设置透明栏if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {Window window = getWindow();window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);window.setStatusBarColor(Color.TRANSPARENT);}

自定义styles.xml:

<resources><style name="AppTheme.Base" parent="Theme.AppCompat.Light"><item name="windowActionBar">false</item><item name="windowNoTitle">true</item></style><style name="BaseAppTheme" parent="AppTheme.Base"><!-- Customize your theme here. --><item name="colorPrimary">@color/colorPrimary</item><item name="colorPrimaryDark">@color/colorPrimaryDark</item><item name="colorAccent">@color/colorAccent</item></style><!-- Base application theme. --><style name="AppTheme" parent="BaseAppTheme"><item name="android:windowTranslucentStatus">true</item></style></resources>

三、具体编码

项目结构图:

注意:因为drawable不支持mp3格式,所以要把mp3文件放在如图新建的raw文件夹中:

准备文件:

图片:上一首,下一首,播放键,暂停键,唱针,唱片,模糊背景图

源码:

main.xml:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="/apk/res/android"xmlns:tools="/tools"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/music_play_background"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="60dp"android:layout_marginTop="10dp"android:layout_alignParentTop="true"android:id="@+id/title"android:orientation="horizontal"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="15dp"android:layout_marginBottom="3dp"android:text="Quit Inside"android:textSize="25dp"android:gravity="center"android:textColor="#ffffff"/></LinearLayout><TextViewandroid:layout_width="match_parent"android:layout_height="0.5dp"android:background="#afafaf"android:layout_below="@+id/title"/><ImageViewandroid:id="@+id/disc"android:layout_width="280dp"android:layout_height="280dp"android:layout_centerHorizontal="true"android:layout_below="@+id/title"android:layout_marginTop="50dp"android:src="@drawable/play_album" /><ImageViewandroid:id="@+id/needle"android:layout_width="120dp"android:layout_height="120dp"android:layout_below="@+id/title"android:src="@drawable/play_needle"android:layout_marginLeft="150dp"/><RelativeLayoutandroid:id="@+id/music1"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_above="@+id/rl"android:layout_marginTop="20dp"android:layout_marginBottom="10dp"android:gravity="center"><SeekBarandroid:id="@+id/music_seek_bar"android:layout_width="240dp"android:layout_height="wrap_content"/><TextSwitcherandroid:id="@+id/text_switcher"android:layout_width="80dp"android:layout_height="50dp"android:layout_toRightOf="@+id/music_seek_bar"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="00:00/2:00"android:textColor="@color/colorAccent"/></TextSwitcher></RelativeLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="70dp"android:gravity="center"android:id="@+id/rl"android:layout_marginBottom="20dp"android:layout_alignParentBottom="true"android:orientation="horizontal"><ImageViewandroid:id="@+id/playing_pre"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:src="@drawable/music_previous" /><ImageViewandroid:id="@+id/playing_play"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:src="@drawable/music_pause" /><ImageViewandroid:id="@+id/playing_next"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:src="@drawable/music_next" /></LinearLayout></RelativeLayout>

MediaServer.java:

按照方法一新建MediaServer.java内容:

package com.example.cungu.musicdemo;import android.app.Service;import android.content.Intent;import android.media.MediaPlayer;import android.os.Binder;import android.os.Environment;import android.os.IBinder;import android.support.annotation.Nullable;import android.util.Log;import java.io.IOException;public class MediaService extends Service {private MediaPlayer mPlayer;/** 绑定服务的实现流程:* 1.服务 onCreate, onBind, onDestroy 方法* 2.onBind 方法需要返回一个 IBinder 对象* 3.如果 Activity 绑定,Activity 就可以取到 IBinder 对象,可以直接调用对象的方法*/// 相同应用内部不同组件绑定,可以使用内部类以及Binder对象来返回。public class MusicController extends Binder {public void play() {mPlayer.start();//开启音乐}public void pause() {mPlayer.pause();//暂停音乐}public long getMusicDuration() {return mPlayer.getDuration();//获取文件的总长度}public long getPosition() {return mPlayer.getCurrentPosition();//获取当前播放进度}public void setPosition (int position) {mPlayer.seekTo(position);//重新设定播放进度}}/*** 当绑定服务的时候,自动回调这个方法* 返回的对象可以直接操作Service内部的内容* @param intent* @return*/@Overridepublic IBinder onBind(Intent intent) {return new MusicController();}@Overridepublic void onCreate() {super.onCreate();mPlayer = MediaPlayer.create(this, R.raw.music1);}/*** 任意一次unbindService()方法,都会触发这个方法* 用于释放一些绑定时使用的资源* @param intent* @return*/@Overridepublic boolean onUnbind(Intent intent) {return super.onUnbind(intent);}@Overridepublic void onDestroy() {if (mPlayer.isPlaying()) {mPlayer.stop();}mPlayer.release();mPlayer = null;super.onDestroy();}}

main.java:

package com.example.cungu.musicdemo;import android.animation.ObjectAnimator;import android.animation.ValueAnimator;import ponentName;import android.content.Intent;import android.content.ServiceConnection;import android.graphics.Bitmap;import android.graphics.Color;import android.os.Build;import android.os.IBinder;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.view.Window;import android.view.WindowManager;import android.view.animation.LinearInterpolator;import android.widget.Button;import android.widget.ImageView;import android.widget.SeekBar;import android.widget.TextSwitcher;import java.text.SimpleDateFormat;import java.util.Date;public class MainActivity extends AppCompatActivity implements View.OnClickListener,Runnable, ServiceConnection, SeekBar.OnSeekBarChangeListener {private ImageView disc,needle,playingPre,playingPlay,playingNext;private ObjectAnimator discAnimation,needleAnimation;//自定义指针和唱盘private boolean isPlaying = true;//0,1 判断是否处于播放状态//声明服务private static final String TAG = MainActivity.class.getSimpleName();private MediaService.MusicController mMusicController;//使用方法:mMusicController.play();播放 mMusicController.pause();暂停private boolean running;private TextSwitcher mSwitcher;private SeekBar mSeekBar;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//设置透明栏if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {Window window = getWindow();window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);window.setStatusBarColor(Color.TRANSPARENT);}//滑动条部分mSeekBar = (SeekBar) findViewById(R.id.music_seek_bar);mSeekBar.setOnSeekBarChangeListener(this);mSwitcher = (TextSwitcher) findViewById(R.id.text_switcher);mSwitcher.setInAnimation(this, android.R.anim.fade_in);mSwitcher.setOutAnimation(this, android.R.anim.fade_out);Intent intent = new Intent(this, MediaService.class);//增加StartService,来增加后台播放功能startService(intent);// 绑定服务,使用context来绑定// 那个界面需要绑定 就用哪个 Activity// 参数1:Intent代表需要绑定哪一个Service// 参数2:ServiceConnection 回调接口,可以接收到Service连接成功和断开的回调,成功就可以取到对象。// 绑定服务 参数2就是服务和指定的对象绑定在一起bindService(intent, this, BIND_AUTO_CREATE);//指针和唱片部分initViews();//定义背景图setAnimations();}private void initViews() {playingPre = (ImageView) findViewById(R.id.playing_pre);playingPlay = (ImageView) findViewById(R.id.playing_play);playingNext = (ImageView) findViewById(R.id.playing_next);disc = (ImageView) findViewById(R.id.disc);needle = (ImageView) findViewById(R.id.needle);playingPre.setOnClickListener(this);playingPlay.setOnClickListener(this);playingNext.setOnClickListener(this);}//动画设置private void setAnimations() {discAnimation = ObjectAnimator.ofFloat(disc, "rotation", 0, 360);discAnimation.setDuration(20000);discAnimation.setInterpolator(new LinearInterpolator());discAnimation.setRepeatCount(ValueAnimator.INFINITE);needleAnimation = ObjectAnimator.ofFloat(needle, "rotation", 0, 25);needle.setPivotX(0);needle.setPivotY(0);needleAnimation.setDuration(800);needleAnimation.setInterpolator(new LinearInterpolator());}@Overridepublic void onClick(View v) {int id = v.getId();switch (id) {//前一曲case R.id.playing_pre:if (discAnimation != null) {discAnimation.end();playing();}break;//播放中case R.id.playing_play:if (isPlaying){playing();}else {if (needleAnimation != null) {needleAnimation.reverse();needleAnimation.end();mMusicController.pause();}if (discAnimation != null && discAnimation.isRunning()) {discAnimation.cancel();mMusicController.pause();float valueAvatar = (float) discAnimation.getAnimatedValue();discAnimation.setFloatValues(valueAvatar, 360f + valueAvatar);}playingPlay.setImageResource(R.drawable.music_play);isPlaying = true;}break;//下一曲case R.id.playing_next:if (discAnimation != null) {discAnimation.end();playing();}break;default:break;}}//播放时动画设置和图片切换private void playing(){needleAnimation.start();discAnimation.start();playingPlay.setImageResource(R.drawable.music_pause);mMusicController.play();//播放isPlaying = false;}//===================================歌曲播放服务================================================@Overrideprotected void onStart() {super.onStart();Thread thread = new Thread(this);thread.start();}@Overrideprotected void onStop() {running = false;super.onStop();}@Overrideprotected void onDestroy() {// 解除绑定unbindService(this);super.onDestroy();}//-----------播放到当前音乐的滑动条及时间设置-------------@Overridepublic void run() {running = true;try {while (running) {if (mMusicController != null) {long musicDuration = mMusicController.getMusicDuration();final long position = mMusicController.getPosition();final Date dateTotal = new Date(musicDuration);final SimpleDateFormat sb = new SimpleDateFormat("mm:ss");mSeekBar.setMax((int) musicDuration);mSeekBar.setProgress((int) position);mSwitcher.post(new Runnable() {@Overridepublic void run() {Date date = new Date(position);String time = sb.format(date) + "/" + sb.format(dateTotal);mSwitcher.setCurrentText(time);}});}Thread.sleep(500);}} catch (InterruptedException e) {e.printStackTrace();}}//-----------------------------//服务绑定与解除绑定的回调/*** 当服务与当前绑定对象,绑定成功,服务onBind方法调用并且返回之后* 回调给这个方法** @param name* @param service IBinder 就是服务 onBind 返回的对象*/@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mMusicController = ((MediaService.MusicController) service);}@Overridepublic void onServiceDisconnected(ComponentName name) {mMusicController = null;}public void btnStopService(View view) {Intent intent = new Intent(this, MediaService.class);stopService(intent);}@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {mMusicController.setPosition(seekBar.getProgress());}}

大功告成!

附:本次项目源码

下载地址:/cungudafa/MusicDemo

注:

1、我在MediaServer里面只放了一首歌曲,可以完善读取手机内存的歌曲,还可以做一个listView界面来显示全部歌曲(需要创建对数据库的调用),like this;

2、真正的网易云模糊背景用了高斯模糊函数,对唱片中图像进行处理,我这里未实现,机智如我,一张模糊背景就处理掉,like this,不够效果不好;

3、还有唱片是我用绘图工具将专辑封面合成的,like this,真正网易云是用图片叠加函数,绘出来的,可以略过手工绘图,我功力不够啊!希望大佬可尝试~

我Ps合成图片:可还行~

若有大佬完善注释功能,请受我一拜!直接去网易云上班吧!

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