styles.xml
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@android:color/holo_red_light</item> <item name="windowNoTitle">true</item> <item name="android:windowTranslucentStatus">true</item> </style> <style name="netease_toolbar_title" parent="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"> <item name="android:textColor">@android:color/white</item> <item name="android:textSize">17sp</item> </style> <style name="netease_toolbar_subtitle" parent="@style/TextAppearance.Widget.AppCompat.Toolbar.Subtitle"> <item name="android:textColor">#eeeeee</item> <item name="android:textSize">14sp</item> </style> <style name="neteasa_music_time_text_sytle"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:textColor">#ccc</item> <item name="android:textSize">9sp</item> </style> <style name="neteasa_music_time_seek_bar_sytle" parent="Widget.AppCompat.SeekBar"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> <item name="android:thumb">@drawable/ic_seekbar_thumb</item> </style> </resources>layout_discview
<?xml version="1.0" encoding="utf-8"?> <com.example.wangxingqi.wangxingqi20180528.widget.DiscView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <!--底盘--> <ImageView android:id="@+id/ivDiscBlackgound" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" /> <!--实现切换--> <android.support.v4.view.ViewPager android:id="@+id/vpDiscContain" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" /> <!--唱针--> <ImageView android:id="@+id/ivNeedle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_needle"/> </com.example.wangxingqi.wangxingqi20180528.widget.DiscView>activity_main
<?xml version="1.0" encoding="utf-8"?> <com.example.wangxingqi.wangxingqi20180528.widget.BackgourndAnimationRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/rootLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" > <android.support.v7.widget.Toolbar android:id="@+id/toolBar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:layout_alignParentTop="true" android:background="@android:color/transparent" app:subtitleTextAppearance="@style/netease_toolbar_subtitle" app:titleTextAppearance="@style/netease_toolbar_title" /> <View android:layout_width="match_parent" android:layout_height="0.5dp" android:layout_below="@id/toolBar" android:background="#33ffffff"/> <include android:id="@+id/discview" layout="@layout/layout_discview" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@+id/rlMusicTime" android:layout_below="@id/toolBar"/> <RelativeLayout android:id="@+id/rlMusicTime" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@+id/llPlayOption" android:layout_marginEnd="15dp" android:layout_marginStart="15dp" android:layout_marginTop="10dp"> <TextView android:id="@+id/tvCurrentTime" style="@style/neteasa_music_time_text_sytle" android:layout_alignParentStart="true" android:layout_centerVertical="true" tools:text="01:34" android:layout_alignParentLeft="true" /> <SeekBar android:id="@+id/musicSeekBar" style="@style/neteasa_music_time_seek_bar_sytle" android:layout_centerInParent="true" android:layout_toEndOf="@id/tvCurrentTime" android:layout_toStartOf="@+id/tvTotalTime" android:paddingEnd="15dp" android:paddingStart="15dp" android:layout_toRightOf="@id/tvCurrentTime" android:layout_toLeftOf="@+id/tvTotalTime" /> <TextView android:id="@+id/tvTotalTime" style="@style/neteasa_music_time_text_sytle" android:layout_alignParentEnd="true" android:layout_centerVertical="true" tools:text="05:34" android:layout_alignParentRight="true" /> </RelativeLayout> <LinearLayout android:id="@+id/llPlayOption" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:gravity="center" android:orientation="horizontal" android:paddingBottom="20dp" android:paddingLeft="20dp" android:paddingRight="20dp" android:paddingTop="10dp" > <ImageView android:id="@+id/ivLast" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_last"/> <ImageView android:id="@+id/ivPlayOrPause" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="10dp" android:layout_marginStart="10dp" android:src="@drawable/ic_play"/> <ImageView android:id="@+id/ivNext" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_next"/> </LinearLayout> </com.example.wangxingqi.wangxingqi20180528.widget.BackgourndAnimationRelativeLayout>layout_disc
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:orientation="vertical"> <ImageView android:id="@+id/ivDisc" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout>MainActivity
package com.example.wangxingqi.wangxingqi20180528; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Handler; import android.os.Message; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.Toolbar; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.ImageView; import android.widget.SeekBar; import android.widget.TextView; import com.example.wangxingqi.wangxingqi20180528.model.MusicData; import com.example.wangxingqi.wangxingqi20180528.service.MusicService; import com.example.wangxingqi.wangxingqi20180528.utils.DisplayUtil; import com.example.wangxingqi.wangxingqi20180528.utils.FastBlurUtil; import com.example.wangxingqi.wangxingqi20180528.widget.BackgourndAnimationRelativeLayout; import com.example.wangxingqi.wangxingqi20180528.widget.DiscView; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import static com.example.wangxingqi.wangxingqi20180528.widget.DiscView.DURATION_NEEDLE_ANIAMTOR; public class MainActivity extends AppCompatActivity implements DiscView.IPlayInfo, View .OnClickListener { private DiscView mDisc; private Toolbar mToolbar; private SeekBar mSeekBar; private ImageView mIvPlayOrPause, mIvNext, mIvLast; private TextView mTvMusicDuration,mTvTotalMusicDuration; private BackgourndAnimationRelativeLayout mRootLayout; public static final int MUSIC_MESSAGE = 0; public static final String PARAM_MUSIC_LIST = "PARAM_MUSIC_LIST"; private Handler mMusicHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); mSeekBar.setProgress(mSeekBar.getProgress() + 1000); // 给文本赋值 mTvMusicDuration.setText(duration2Time(mSeekBar.getProgress())); startUpdateSeekBarProgress(); } }; private MusicReceiver mMusicReceiver = new MusicReceiver(); private List<MusicData> mMusicDatas = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initMusicDatas(); initView(); initMusicReceiver(); makeStatusBarTransparent(); } private void initMusicReceiver() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(MusicService.ACTION_STATUS_MUSIC_PLAY); intentFilter.addAction(MusicService.ACTION_STATUS_MUSIC_PAUSE); intentFilter.addAction(MusicService.ACTION_STATUS_MUSIC_DURATION); intentFilter.addAction(MusicService.ACTION_STATUS_MUSIC_COMPLETE); /*注册本地广播*/ LocalBroadcastManager.getInstance(this).registerReceiver(mMusicReceiver,intentFilter); } private void initView() { mDisc = (DiscView) findViewById(R.id.discview); mIvNext = (ImageView) findViewById(R.id.ivNext); mIvLast = (ImageView) findViewById(R.id.ivLast); mIvPlayOrPause = (ImageView) findViewById(R.id.ivPlayOrPause); mTvMusicDuration = (TextView) findViewById(R.id.tvCurrentTime); mTvTotalMusicDuration = (TextView) findViewById(R.id.tvTotalTime); mSeekBar = (SeekBar) findViewById(R.id.musicSeekBar); mRootLayout = (BackgourndAnimationRelativeLayout) findViewById(R.id.rootLayout); mToolbar = (Toolbar) findViewById(R.id.toolBar); setSupportActionBar(mToolbar); mDisc.setPlayInfoListener(this); mIvLast.setOnClickListener(this); mIvNext.setOnClickListener(this); mIvPlayOrPause.setOnClickListener(this); mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { mTvMusicDuration.setText(duration2Time(progress)); } @Override public void onStartTrackingTouch(SeekBar seekBar) { stopUpdateSeekBarProgree(); } @Override public void onStopTrackingTouch(SeekBar seekBar) { seekTo(seekBar.getProgress()); startUpdateSeekBarProgress(); } }); mTvMusicDuration.setText(duration2Time(0)); mTvTotalMusicDuration.setText(duration2Time(0)); mDisc.setMusicDataList(mMusicDatas); } private void stopUpdateSeekBarProgree() { mMusicHandler.removeMessages(MUSIC_MESSAGE); } /*设置透明状态栏*/ private void makeStatusBarTransparent() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = getWindow(); window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 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); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } } private void initMusicDatas() { MusicData musicData1 = new MusicData(R.raw.music1, R.raw.ic_music1, "寻", "三亩地"); MusicData musicData2 = new MusicData(R.raw.music2, R.raw.ic_music2, "Nightingale", "YANI"); MusicData musicData3 = new MusicData(R.raw.music3, R.raw.ic_music3, "Cornfield Chase", "Hans Zimmer"); mMusicDatas.add(musicData1); mMusicDatas.add(musicData2); mMusicDatas.add(musicData3); Intent intent = new Intent(this, MusicService.class); intent.putExtra(PARAM_MUSIC_LIST, (Serializable) mMusicDatas); startService(intent); } private void try2UpdateMusicPicBackground(final int musicPicRes) { if (mRootLayout.isNeed2UpdateBackground(musicPicRes)) { new Thread(new Runnable() { @Override public void run() { final Drawable foregroundDrawable = getForegroundDrawable(musicPicRes); runOnUiThread(new Runnable() { @Override public void run() { mRootLayout.setForeground(foregroundDrawable); mRootLayout.beginAnimation(); } }); } }).start(); } } private Drawable getForegroundDrawable(int musicPicRes) { /*得到屏幕的宽高比,以便按比例切割图片一部分*/ final float widthHeightSize = (float) (DisplayUtil.getScreenWidth(MainActivity.this) * 1.0 / DisplayUtil.getScreenHeight(this) * 1.0); Bitmap bitmap = getForegroundBitmap(musicPicRes); int cropBitmapWidth = (int) (widthHeightSize * bitmap.getHeight()); int cropBitmapWidthX = (int) ((bitmap.getWidth() - cropBitmapWidth) / 2.0); /*切割部分图片*/ Bitmap cropBitmap = Bitmap.createBitmap(bitmap, cropBitmapWidthX, 0, cropBitmapWidth, bitmap.getHeight()); /*缩小图片*/ Bitmap scaleBitmap = Bitmap.createScaledBitmap(cropBitmap, bitmap.getWidth() / 50, bitmap .getHeight() / 50, false); /*模糊化*/ final Bitmap blurBitmap = FastBlurUtil.doBlur(scaleBitmap, 8, true); final Drawable foregroundDrawable = new BitmapDrawable(blurBitmap); /*加入灰色遮罩层,避免图片过亮影响其他控件*/ foregroundDrawable.setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY); return foregroundDrawable; } private Bitmap getForegroundBitmap(int musicPicRes) { int screenWidth = DisplayUtil.getScreenWidth(this); int screenHeight = DisplayUtil.getScreenHeight(this); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), musicPicRes, options); int imageWidth = options.outWidth; int imageHeight = options.outHeight; if (imageWidth < screenWidth && imageHeight < screenHeight) { return BitmapFactory.decodeResource(getResources(), musicPicRes); } int sample = 2; int sampleX = imageWidth / DisplayUtil.getScreenWidth(this); int sampleY = imageHeight / DisplayUtil.getScreenHeight(this); if (sampleX > sampleY && sampleY > 1) { sample = sampleX; } else if (sampleY > sampleX && sampleX > 1) { sample = sampleY; } options.inJustDecodeBounds = false; options.inSampleSize = sample; options.inPreferredConfig = Bitmap.Config.RGB_565; return BitmapFactory.decodeResource(getResources(), musicPicRes, options); } @Override public void onMusicInfoChanged(String musicName, String musicAuthor) { getSupportActionBar().setTitle(musicName); getSupportActionBar().setSubtitle(musicAuthor); } @Override public void onMusicPicChanged(int musicPicRes) { try2UpdateMusicPicBackground(musicPicRes); } @Override public void onMusicChanged(DiscView.MusicChangedStatus musicChangedStatus) { switch (musicChangedStatus) { case PLAY:{ play(); break; } case PAUSE:{ pause(); break; } case NEXT:{ next(); break; } case LAST:{ last(); break; } case STOP:{ stop(); break; } } } @Override public void onClick(View v) { if (v == mIvPlayOrPause) { mDisc.playOrPause(); } else if (v == mIvNext) { mDisc.next(); } else if (v == mIvLast) { mDisc.last(); } } private void play() { optMusic(MusicService.ACTION_OPT_MUSIC_PLAY); startUpdateSeekBarProgress(); } private void pause() { optMusic(MusicService.ACTION_OPT_MUSIC_PAUSE); stopUpdateSeekBarProgree(); } private void stop() { stopUpdateSeekBarProgree(); mIvPlayOrPause.setImageResource(R.drawable.ic_play); mTvMusicDuration.setText(duration2Time(0)); mTvTotalMusicDuration.setText(duration2Time(0)); mSeekBar.setProgress(0); } private void next() { mRootLayout.postDelayed(new Runnable() { @Override public void run() { optMusic(MusicService.ACTION_OPT_MUSIC_NEXT); } }, DURATION_NEEDLE_ANIAMTOR); stopUpdateSeekBarProgree(); mTvMusicDuration.setText(duration2Time(0)); mTvTotalMusicDuration.setText(duration2Time(0)); } private void last() { mRootLayout.postDelayed(new Runnable() { @Override public void run() { optMusic(MusicService.ACTION_OPT_MUSIC_LAST); } }, DURATION_NEEDLE_ANIAMTOR); stopUpdateSeekBarProgree(); mTvMusicDuration.setText(duration2Time(0)); mTvTotalMusicDuration.setText(duration2Time(0)); } private void complete(boolean isOver) { if (isOver) { mDisc.stop(); } else { mDisc.next(); } } private void optMusic(final String action) { LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(action)); } private void seekTo(int position) { Intent intent = new Intent(MusicService.ACTION_OPT_MUSIC_SEEK_TO); intent.putExtra(MusicService.PARAM_MUSIC_SEEK_TO,position); LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } private void startUpdateSeekBarProgress() { /*避免重复发送Message*/ stopUpdateSeekBarProgree(); mMusicHandler.sendEmptyMessageDelayed(0,1000); } /*根据时长格式化称时间文本*/ private String duration2Time(int duration) { int min = duration / 1000 / 60; int sec = duration / 1000 % 60; return (min < 10 ? "0" + min : min + "") + ":" + (sec < 10 ? "0" + sec : sec + ""); } private void updateMusicDurationInfo(int totalDuration) { mSeekBar.setProgress(0); mSeekBar.setMax(totalDuration); mTvTotalMusicDuration.setText(duration2Time(totalDuration)); mTvMusicDuration.setText(duration2Time(0)); startUpdateSeekBarProgress(); } class MusicReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(MusicService.ACTION_STATUS_MUSIC_PLAY)) { mIvPlayOrPause.setImageResource(R.drawable.ic_pause); int currentPosition = intent.getIntExtra(MusicService.PARAM_MUSIC_CURRENT_POSITION, 0); mSeekBar.setProgress(currentPosition); if(!mDisc.isPlaying()){ mDisc.playOrPause(); } } else if (action.equals(MusicService.ACTION_STATUS_MUSIC_PAUSE)) { mIvPlayOrPause.setImageResource(R.drawable.ic_play); if (mDisc.isPlaying()) { mDisc.playOrPause(); } } else if (action.equals(MusicService.ACTION_STATUS_MUSIC_DURATION)) { int duration = intent.getIntExtra(MusicService.PARAM_MUSIC_DURATION, 0); updateMusicDurationInfo(duration); } else if (action.equals(MusicService.ACTION_STATUS_MUSIC_COMPLETE)) { boolean isOver = intent.getBooleanExtra(MusicService.PARAM_MUSIC_IS_OVER, true); complete(isOver); } } } @Override protected void onDestroy() { super.onDestroy(); LocalBroadcastManager.getInstance(this).unregisterReceiver(mMusicReceiver); } }DiscView package com.example.wangxingqi.wangxingqi20180528.widget; import android.animation.Animator; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.support.v4.graphics.drawable.RoundedBitmapDrawable; import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; import android.view.animation.LinearInterpolator; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.Toast; import com.example.wangxingqi.wangxingqi20180528.R; import com.example.wangxingqi.wangxingqi20180528.model.MusicData; import com.example.wangxingqi.wangxingqi20180528.utils.DisplayUtil; import java.util.ArrayList; import java.util.List; /** * Created by AchillesL on 2016/11/15. */ public class DiscView extends RelativeLayout { private ImageView mIvNeedle; private ViewPager mVpContain; private ViewPagerAdapter mViewPagerAdapter; private ObjectAnimator mNeedleAnimator; private List<View> mDiscLayouts = new ArrayList<>(); private List<MusicData> mMusicDatas = new ArrayList<>(); private List<ObjectAnimator> mDiscAnimators = new ArrayList<>(); /*标记ViewPager是否处于偏移的状态*/ private boolean mViewPagerIsOffset = false; /*标记唱针复位后,是否需要重新偏移到唱片处*/ private boolean mIsNeed2StartPlayAnimator = false; private MusicStatus musicStatus = MusicStatus.STOP; public static final int DURATION_NEEDLE_ANIAMTOR = 500; private NeedleAnimatorStatus needleAnimatorStatus = NeedleAnimatorStatus.IN_FAR_END; private IPlayInfo mIPlayInfo; private int mScreenWidth, mScreenHeight; /*唱针当前所处的状态*/ private enum NeedleAnimatorStatus { /*移动时:从唱盘往远处移动*/ TO_FAR_END, /*移动时:从远处往唱盘移动*/ TO_NEAR_END, /*静止时:离开唱盘*/ IN_FAR_END, /*静止时:贴近唱盘*/ IN_NEAR_END } /*音乐当前的状态:只有播放、暂停、停止三种*/ public enum MusicStatus { PLAY, PAUSE, STOP } /*DiscView需要触发的音乐切换状态:播放、暂停、上/下一首、停止*/ public enum MusicChangedStatus { PLAY, PAUSE, NEXT, LAST, STOP } public interface IPlayInfo { /*用于更新标题栏变化*/ public void onMusicInfoChanged(String musicName, String musicAuthor); /*用于更新背景图片*/ public void onMusicPicChanged(int musicPicRes); /*用于更新音乐播放状态*/ public void onMusicChanged(MusicChangedStatus musicChangedStatus); } public DiscView(Context context) { this(context, null); } public DiscView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DiscView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mScreenWidth = DisplayUtil.getScreenWidth(context); mScreenHeight = DisplayUtil.getScreenHeight(context); } @Override protected void onFinishInflate() { super.onFinishInflate(); initDiscBlackground(); initViewPager(); initNeedle(); initObjectAnimator(); } private void initDiscBlackground() { ImageView mDiscBlackground = (ImageView) findViewById(R.id.ivDiscBlackgound); mDiscBlackground.setImageDrawable(getDiscBlackgroundDrawable()); int marginTop = (int) (DisplayUtil.SCALE_DISC_MARGIN_TOP * mScreenHeight); LayoutParams layoutParams = (LayoutParams) mDiscBlackground .getLayoutParams(); layoutParams.setMargins(0, marginTop, 0, 0); mDiscBlackground.setLayoutParams(layoutParams); } private void initViewPager() { mViewPagerAdapter = new ViewPagerAdapter(); mVpContain = (ViewPager) findViewById(R.id.vpDiscContain); mVpContain.setOverScrollMode(View.OVER_SCROLL_NEVER); mVpContain.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { int lastPositionOffsetPixels = 0; int currentItem = 0; @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { //左滑 if (lastPositionOffsetPixels > positionOffsetPixels) { if (positionOffset < 0.5) { notifyMusicInfoChanged(position); } else { notifyMusicInfoChanged(mVpContain.getCurrentItem()); } } //右滑 else if (lastPositionOffsetPixels < positionOffsetPixels) { if (positionOffset > 0.5) { notifyMusicInfoChanged(position + 1); } else { notifyMusicInfoChanged(position); } } lastPositionOffsetPixels = positionOffsetPixels; } @Override public void onPageSelected(int position) { resetOtherDiscAnimation(position); notifyMusicPicChanged(position); if (position > currentItem) { notifyMusicStatusChanged(MusicChangedStatus.NEXT); } else { notifyMusicStatusChanged(MusicChangedStatus.LAST); } currentItem = position; } @Override public void onPageScrollStateChanged(int state) { doWithAnimatorOnPageScroll(state); } }); mVpContain.setAdapter(mViewPagerAdapter); LayoutParams layoutParams = (LayoutParams) mVpContain.getLayoutParams(); int marginTop = (int) (DisplayUtil.SCALE_DISC_MARGIN_TOP * mScreenHeight); layoutParams.setMargins(0, marginTop, 0, 0); mVpContain.setLayoutParams(layoutParams); } /** * 取消其他页面上的动画,并将图片旋转角度复原 */ private void resetOtherDiscAnimation(int position) { for (int i = 0; i < mDiscLayouts.size(); i++) { if (position == i) continue; mDiscAnimators.get(position).cancel(); ImageView imageView = (ImageView) mDiscLayouts.get(i).findViewById(R.id.ivDisc); imageView.setRotation(0); } } private void doWithAnimatorOnPageScroll(int state) { switch (state) { case ViewPager.SCROLL_STATE_IDLE: case ViewPager.SCROLL_STATE_SETTLING: { mViewPagerIsOffset = false; if (musicStatus == MusicStatus.PLAY) { playAnimator(); } break; } case ViewPager.SCROLL_STATE_DRAGGING: { mViewPagerIsOffset = true; pauseAnimator(); break; } } } private void initNeedle() { mIvNeedle = (ImageView) findViewById(R.id.ivNeedle); int needleWidth = (int) (DisplayUtil.SCALE_NEEDLE_WIDTH * mScreenWidth); int needleHeight = (int) (DisplayUtil.SCALE_NEEDLE_HEIGHT * mScreenHeight); /*设置手柄的外边距为负数,让其隐藏一部分*/ int marginTop = (int) (DisplayUtil.SCALE_NEEDLE_MARGIN_TOP * mScreenHeight) * -1; int marginLeft = (int) (DisplayUtil.SCALE_NEEDLE_MARGIN_LEFT * mScreenWidth); Bitmap originBitmap = BitmapFactory.decodeResource(getResources(), R.drawable .ic_needle); Bitmap bitmap = Bitmap.createScaledBitmap(originBitmap, needleWidth, needleHeight, false); LayoutParams layoutParams = (LayoutParams) mIvNeedle.getLayoutParams(); layoutParams.setMargins(marginLeft, marginTop, 0, 0); int pivotX = (int) (DisplayUtil.SCALE_NEEDLE_PIVOT_X * mScreenWidth); int pivotY = (int) (DisplayUtil.SCALE_NEEDLE_PIVOT_Y * mScreenWidth); mIvNeedle.setPivotX(pivotX); mIvNeedle.setPivotY(pivotY); mIvNeedle.setRotation(DisplayUtil.ROTATION_INIT_NEEDLE); mIvNeedle.setImageBitmap(bitmap); mIvNeedle.setLayoutParams(layoutParams); } private void initObjectAnimator() { mNeedleAnimator = ObjectAnimator.ofFloat(mIvNeedle, View.ROTATION, DisplayUtil .ROTATION_INIT_NEEDLE, 0); mNeedleAnimator.setDuration(DURATION_NEEDLE_ANIAMTOR); mNeedleAnimator.setInterpolator(new AccelerateInterpolator()); mNeedleAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { /** * 根据动画开始前NeedleAnimatorStatus的状态, * 即可得出动画进行时NeedleAnimatorStatus的状态 * */ if (needleAnimatorStatus == NeedleAnimatorStatus.IN_FAR_END) { needleAnimatorStatus = NeedleAnimatorStatus.TO_NEAR_END; } else if (needleAnimatorStatus == NeedleAnimatorStatus.IN_NEAR_END) { needleAnimatorStatus = NeedleAnimatorStatus.TO_FAR_END; } } @Override public void onAnimationEnd(Animator animator) { if (needleAnimatorStatus == NeedleAnimatorStatus.TO_NEAR_END) { needleAnimatorStatus = NeedleAnimatorStatus.IN_NEAR_END; int index = mVpContain.getCurrentItem(); playDiscAnimator(index); musicStatus = MusicStatus.PLAY; } else if (needleAnimatorStatus == NeedleAnimatorStatus.TO_FAR_END) { needleAnimatorStatus = NeedleAnimatorStatus.IN_FAR_END; if (musicStatus == MusicStatus.STOP) { mIsNeed2StartPlayAnimator = true; } } if (mIsNeed2StartPlayAnimator) { mIsNeed2StartPlayAnimator = false; /** * 只有在ViewPager不处于偏移状态时,才开始唱盘旋转动画 * */ if (!mViewPagerIsOffset) { /*延时500ms*/ DiscView.this.postDelayed(new Runnable() { @Override public void run() { playAnimator(); } }, 50); } } } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); } public void setPlayInfoListener(IPlayInfo listener) { this.mIPlayInfo = listener; } /*得到唱盘背后半透明的圆形背景*/ private Drawable getDiscBlackgroundDrawable() { int discSize = (int) (mScreenWidth * DisplayUtil.SCALE_DISC_SIZE); Bitmap bitmapDisc = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R .drawable.ic_disc_blackground), discSize, discSize, false); RoundedBitmapDrawable roundDiscDrawable = RoundedBitmapDrawableFactory.create (getResources(), bitmapDisc); return roundDiscDrawable; } /** * 得到唱盘图片 * 唱盘图片由空心圆盘及音乐专辑图片“合成”得到 */ private Drawable getDiscDrawable(int musicPicRes) { int discSize = (int) (mScreenWidth * DisplayUtil.SCALE_DISC_SIZE); int musicPicSize = (int) (mScreenWidth * DisplayUtil.SCALE_MUSIC_PIC_SIZE); Bitmap bitmapDisc = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R .drawable.ic_disc), discSize, discSize, false); Bitmap bitmapMusicPic = getMusicPicBitmap(musicPicSize,musicPicRes); BitmapDrawable discDrawable = new BitmapDrawable(bitmapDisc); RoundedBitmapDrawable roundMusicDrawable = RoundedBitmapDrawableFactory.create (getResources(), bitmapMusicPic); //抗锯齿 discDrawable.setAntiAlias(true); roundMusicDrawable.setAntiAlias(true); Drawable[] drawables = new Drawable[2]; drawables[0] = roundMusicDrawable; drawables[1] = discDrawable; LayerDrawable layerDrawable = new LayerDrawable(drawables); int musicPicMargin = (int) ((DisplayUtil.SCALE_DISC_SIZE - DisplayUtil .SCALE_MUSIC_PIC_SIZE) * mScreenWidth / 2); //调整专辑图片的四周边距,让其显示在正中 layerDrawable.setLayerInset(0, musicPicMargin, musicPicMargin, musicPicMargin, musicPicMargin); return layerDrawable; } private Bitmap getMusicPicBitmap(int musicPicSize, int musicPicRes) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(),musicPicRes,options); int imageWidth = options.outWidth; int sample = imageWidth / musicPicSize; int dstSample = 1; if (sample > dstSample) { dstSample = sample; } options.inJustDecodeBounds = false; //设置图片采样率 options.inSampleSize = dstSample; //设置图片解码格式 options.inPreferredConfig = Bitmap.Config.RGB_565; return Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), musicPicRes, options), musicPicSize, musicPicSize, true); } public void setMusicDataList(List<MusicData> musicDataList) { if (musicDataList.isEmpty()) return; mDiscLayouts.clear(); mMusicDatas.clear(); mDiscAnimators.clear(); mMusicDatas.addAll(musicDataList); int i = 0; for (MusicData musicData : mMusicDatas) { View discLayout = LayoutInflater.from(getContext()).inflate(R.layout.layout_disc, mVpContain, false); ImageView disc = (ImageView) discLayout.findViewById(R.id.ivDisc); disc.setImageDrawable(getDiscDrawable(musicData.getMusicPicRes())); mDiscAnimators.add(getDiscObjectAnimator(disc, i++)); mDiscLayouts.add(discLayout); } mViewPagerAdapter.notifyDataSetChanged(); MusicData musicData = mMusicDatas.get(0); if (mIPlayInfo != null) { mIPlayInfo.onMusicInfoChanged(musicData.getMusicName(), musicData.getMusicAuthor()); mIPlayInfo.onMusicPicChanged(musicData.getMusicPicRes()); } } private ObjectAnimator getDiscObjectAnimator(ImageView disc, final int i) { ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(disc, View.ROTATION, 0, 360); objectAnimator.setRepeatCount(ValueAnimator.INFINITE); objectAnimator.setDuration(20 * 1000); objectAnimator.setInterpolator(new LinearInterpolator()); return objectAnimator; } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); } /*播放动画*/ private void playAnimator() { /*唱针处于远端时,直接播放动画*/ if (needleAnimatorStatus == NeedleAnimatorStatus.IN_FAR_END) { mNeedleAnimator.start(); } /*唱针处于往远端移动时,设置标记,等动画结束后再播放动画*/ else if (needleAnimatorStatus == NeedleAnimatorStatus.TO_FAR_END) { mIsNeed2StartPlayAnimator = true; } } /*暂停动画*/ private void pauseAnimator() { /*播放时暂停动画*/ if (needleAnimatorStatus == NeedleAnimatorStatus.IN_NEAR_END) { int index = mVpContain.getCurrentItem(); pauseDiscAnimatior(index); } /*唱针往唱盘移动时暂停动画*/ else if (needleAnimatorStatus == NeedleAnimatorStatus.TO_NEAR_END) { mNeedleAnimator.reverse(); /** * 若动画在没结束时执行reverse方法,则不会执行监听器的onStart方法,此时需要手动设置 * */ needleAnimatorStatus = NeedleAnimatorStatus.TO_FAR_END; } /** * 动画可能执行多次,只有音乐处于停止 / 暂停状态时,才执行暂停命令 * */ if (musicStatus == MusicStatus.STOP) { notifyMusicStatusChanged(MusicChangedStatus.STOP); }else if (musicStatus == MusicStatus.PAUSE) { notifyMusicStatusChanged(MusicChangedStatus.PAUSE); } } /*播放唱盘动画*/ private void playDiscAnimator(int index) { ObjectAnimator objectAnimator = mDiscAnimators.get(index); if (objectAnimator.isPaused()) { objectAnimator.resume(); } else { objectAnimator.start(); } /** * 唱盘动画可能执行多次,只有不是音乐不在播放状态,在回调执行播放 * */ if (musicStatus != MusicStatus.PLAY) { notifyMusicStatusChanged(MusicChangedStatus.PLAY); } } /*暂停唱盘动画*/ private void pauseDiscAnimatior(int index) { ObjectAnimator objectAnimator = mDiscAnimators.get(index); objectAnimator.pause(); mNeedleAnimator.reverse(); } public void notifyMusicInfoChanged(int position) { if (mIPlayInfo != null) { MusicData musicData = mMusicDatas.get(position); mIPlayInfo.onMusicInfoChanged(musicData.getMusicName(), musicData.getMusicAuthor()); } } public void notifyMusicPicChanged(int position) { if (mIPlayInfo != null) { MusicData musicData = mMusicDatas.get(position); mIPlayInfo.onMusicPicChanged(musicData.getMusicPicRes()); } } public void notifyMusicStatusChanged(MusicChangedStatus musicChangedStatus) { if (mIPlayInfo != null) { mIPlayInfo.onMusicChanged(musicChangedStatus); } } private void play() { playAnimator(); } private void pause() { musicStatus = MusicStatus.PAUSE; pauseAnimator(); } public void stop() { musicStatus = MusicStatus.STOP; pauseAnimator(); } public void playOrPause() { if (musicStatus == MusicStatus.PLAY) { pause(); } else { play(); } } public void next() { int currentItem = mVpContain.getCurrentItem(); if (currentItem == mMusicDatas.size() - 1) { Toast.makeText(getContext(), "已经到达最后一首", Toast.LENGTH_SHORT).show(); } else { selectMusicWithButton(); mVpContain.setCurrentItem(currentItem + 1, true); } } public void last() { int currentItem = mVpContain.getCurrentItem(); if (currentItem == 0) { Toast.makeText(getContext(), "已经到达第一首", Toast.LENGTH_SHORT).show(); } else { selectMusicWithButton(); mVpContain.setCurrentItem(currentItem - 1, true); } } public boolean isPlaying() { return musicStatus == MusicStatus.PLAY; } private void selectMusicWithButton() { if (musicStatus == MusicStatus.PLAY) { mIsNeed2StartPlayAnimator = true; pauseAnimator(); } else if (musicStatus == MusicStatus.PAUSE) { play(); } } class ViewPagerAdapter extends PagerAdapter { @Override public Object instantiateItem(ViewGroup container, int position) { View discLayout = mDiscLayouts.get(position); container.addView(discLayout); return discLayout; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView(mDiscLayouts.get(position)); } @Override public int getCount() { return mDiscLayouts.size(); } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } } } BackgourndAnimationRelativeLayout package com.example.wangxingqi.wangxingqi20180528.widget; import android.animation.Animator; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.os.Build; import android.support.annotation.RequiresApi; import android.util.AttributeSet; import android.view.animation.AccelerateInterpolator; import android.widget.RelativeLayout; import com.example.wangxingqi.wangxingqi20180528.R; /** * Created by AchillesL on 2016/11/18. */ /** * 自定义一个控件,继承RelativeLayout * */ public class BackgourndAnimationRelativeLayout extends RelativeLayout { private final int DURATION_ANIMATION = 500; private final int INDEX_BACKGROUND = 0; private final int INDEX_FOREGROUND = 1; /** * LayerDrawable[0]: background drawable * LayerDrawable[1]: foreground drawable */ private LayerDrawable layerDrawable; private ObjectAnimator objectAnimator; private int musicPicRes = -1; @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public BackgourndAnimationRelativeLayout(Context context) { this(context, null); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public BackgourndAnimationRelativeLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public BackgourndAnimationRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initLayerDrawable(); initObjectAnimator(); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void initLayerDrawable() { Drawable backgroundDrawable = getContext().getDrawable(R.drawable.ic_blackground); Drawable[] drawables = new Drawable[2]; /*初始化时先将前景与背景颜色设为一致*/ drawables[INDEX_BACKGROUND] = backgroundDrawable; drawables[INDEX_FOREGROUND] = backgroundDrawable; layerDrawable = new LayerDrawable(drawables); } @SuppressLint("ObjectAnimatorBinding") private void initObjectAnimator() { objectAnimator = ObjectAnimator.ofFloat(this, "number", 0f, 1.0f); objectAnimator.setDuration(DURATION_ANIMATION); objectAnimator.setInterpolator(new AccelerateInterpolator()); objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) @Override public void onAnimationUpdate(ValueAnimator animation) { int foregroundAlpha = (int) ((float) animation.getAnimatedValue() * 255); /*动态设置Drawable的透明度,让前景图逐渐显示*/ layerDrawable.getDrawable(INDEX_FOREGROUND).setAlpha(foregroundAlpha); BackgourndAnimationRelativeLayout.this.setBackground(layerDrawable); } }); objectAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @RequiresApi(api = Build.VERSION_CODES.M) @Override public void onAnimationEnd(Animator animation) { /*动画结束后,记得将原来的背景图及时更新*/ layerDrawable.setDrawable(INDEX_BACKGROUND, layerDrawable.getDrawable( INDEX_FOREGROUND)); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } @RequiresApi(api = Build.VERSION_CODES.M) public void setForeground(Drawable drawable) { layerDrawable.setDrawable(INDEX_FOREGROUND, drawable); } //对外提供方法,用于开始渐变动画 public void beginAnimation() { objectAnimator.start(); } public boolean isNeed2UpdateBackground(int musicPicRes) { if (this.musicPicRes == -1) return true; if (musicPicRes != this.musicPicRes) { return true; } return false; } }MusicData
package com.example.wangxingqi.wangxingqi20180528.model; import android.os.Parcel; import android.os.Parcelable; import java.io.Serializable; /** * Created by AchillesL on 2016/11/15. */ public class MusicData implements Serializable{ /*音乐资源id*/ private int mMusicRes; /*专辑图片id*/ private int mMusicPicRes; /*音乐名称*/ private String mMusicName; /*作者*/ private String mMusicAuthor; public MusicData(int mMusicRes, int mMusicPicRes, String mMusicName, String mMusicAuthor) { this.mMusicRes = mMusicRes; this.mMusicPicRes = mMusicPicRes; this.mMusicName = mMusicName; this.mMusicAuthor = mMusicAuthor; } public int getMusicRes() { return mMusicRes; } public int getMusicPicRes() { return mMusicPicRes; } public String getMusicName() { return mMusicName; } public String getMusicAuthor() { return mMusicAuthor; } } MusicService package com.example.wangxingqi.wangxingqi20180528.service; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.MediaPlayer; import android.os.IBinder; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import com.example.wangxingqi.wangxingqi20180528.MainActivity; import com.example.wangxingqi.wangxingqi20180528.model.MusicData; import java.util.ArrayList; import java.util.List; /** * Created by AchillesL on 2016/11/18. */ public class MusicService extends Service implements MediaPlayer.OnCompletionListener { /*操作指令*/ public static final String ACTION_OPT_MUSIC_PLAY = "ACTION_OPT_MUSIC_PLAY"; public static final String ACTION_OPT_MUSIC_PAUSE = "ACTION_OPT_MUSIC_PAUSE"; public static final String ACTION_OPT_MUSIC_NEXT = "ACTION_OPT_MUSIC_NEXT"; public static final String ACTION_OPT_MUSIC_LAST = "ACTION_OPT_MUSIC_LAST"; public static final String ACTION_OPT_MUSIC_SEEK_TO = "ACTION_OPT_MUSIC_SEEK_TO"; /*状态指令*/ public static final String ACTION_STATUS_MUSIC_PLAY = "ACTION_STATUS_MUSIC_PLAY"; public static final String ACTION_STATUS_MUSIC_PAUSE = "ACTION_STATUS_MUSIC_PAUSE"; public static final String ACTION_STATUS_MUSIC_COMPLETE = "ACTION_STATUS_MUSIC_COMPLETE"; public static final String ACTION_STATUS_MUSIC_DURATION = "ACTION_STATUS_MUSIC_DURATION"; public static final String PARAM_MUSIC_DURATION = "PARAM_MUSIC_DURATION"; public static final String PARAM_MUSIC_SEEK_TO = "PARAM_MUSIC_SEEK_TO"; public static final String PARAM_MUSIC_CURRENT_POSITION = "PARAM_MUSIC_CURRENT_POSITION"; public static final String PARAM_MUSIC_IS_OVER = "PARAM_MUSIC_IS_OVER"; private int mCurrentMusicIndex = 0; private boolean mIsMusicPause = false; private List<MusicData> mMusicDatas = new ArrayList<>(); private MusicReceiver mMusicReceiver = new MusicReceiver(); private MediaPlayer mMediaPlayer = new MediaPlayer(); @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { initMusicDatas(intent); return super.onStartCommand(intent, flags, startId); } @Override public void onCreate() { super.onCreate(); initBoardCastReceiver(); } private void initMusicDatas(Intent intent) { if (intent == null) return; List<MusicData> musicDatas = (List<MusicData>) intent.getSerializableExtra(MainActivity.PARAM_MUSIC_LIST); mMusicDatas.addAll(musicDatas); } private void initBoardCastReceiver() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ACTION_OPT_MUSIC_PLAY); intentFilter.addAction(ACTION_OPT_MUSIC_PAUSE); intentFilter.addAction(ACTION_OPT_MUSIC_NEXT); intentFilter.addAction(ACTION_OPT_MUSIC_LAST); intentFilter.addAction(ACTION_OPT_MUSIC_SEEK_TO); LocalBroadcastManager.getInstance(this).registerReceiver(mMusicReceiver,intentFilter); } @Override public void onDestroy() { super.onDestroy(); mMediaPlayer.release(); mMediaPlayer = null; LocalBroadcastManager.getInstance(this).unregisterReceiver(mMusicReceiver); } private void play(final int index) { if (index >= mMusicDatas.size()) return; if (mCurrentMusicIndex == index && mIsMusicPause) { mMediaPlayer.start(); } else { mMediaPlayer.stop(); mMediaPlayer = null; mMediaPlayer = MediaPlayer.create(getApplicationContext(), mMusicDatas.get(index) .getMusicRes()); mMediaPlayer.start(); mMediaPlayer.setOnCompletionListener(this); mCurrentMusicIndex = index; mIsMusicPause = false; int duration = mMediaPlayer.getDuration(); sendMusicDurationBroadCast(duration); } sendMusicStatusBroadCast(ACTION_STATUS_MUSIC_PLAY); } private void pause() { mMediaPlayer.pause(); mIsMusicPause = true; sendMusicStatusBroadCast(ACTION_STATUS_MUSIC_PAUSE); } private void stop() { mMediaPlayer.stop(); } private void next() { if (mCurrentMusicIndex + 1 < mMusicDatas.size()) { play(mCurrentMusicIndex + 1); } else { stop(); } } private void last() { if (mCurrentMusicIndex != 0) { play(mCurrentMusicIndex - 1); } } private void seekTo(Intent intent) { if (mMediaPlayer.isPlaying()) { int position = intent.getIntExtra(PARAM_MUSIC_SEEK_TO, 0); mMediaPlayer.seekTo(position); } } @Override public void onCompletion(MediaPlayer mp) { sendMusicCompleteBroadCast(); } class MusicReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(ACTION_OPT_MUSIC_PLAY)) { play(mCurrentMusicIndex); } else if (action.equals(ACTION_OPT_MUSIC_PAUSE)) { pause(); } else if (action.equals(ACTION_OPT_MUSIC_LAST)) { last(); } else if (action.equals(ACTION_OPT_MUSIC_NEXT)) { next(); } else if (action.equals(ACTION_OPT_MUSIC_SEEK_TO)) { seekTo(intent); } } } private void sendMusicCompleteBroadCast() { Intent intent = new Intent(ACTION_STATUS_MUSIC_COMPLETE); intent.putExtra(PARAM_MUSIC_IS_OVER, (mCurrentMusicIndex == mMusicDatas.size() - 1)); LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } private void sendMusicDurationBroadCast(int duration) { Intent intent = new Intent(ACTION_STATUS_MUSIC_DURATION); intent.putExtra(PARAM_MUSIC_DURATION, duration); LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } private void sendMusicStatusBroadCast(String action) { Intent intent = new Intent(action); if (action.equals(ACTION_STATUS_MUSIC_PLAY)) { intent.putExtra(PARAM_MUSIC_CURRENT_POSITION,mMediaPlayer.getCurrentPosition()); } LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } } FastBlurUtil package com.example.wangxingqi.wangxingqi20180528.utils; import android.graphics.Bitmap; /** * Created by jay on 11/7/15. */ public class FastBlurUtil { public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) { Bitmap bitmap; if (canReuseInBitmap) { bitmap = sentBitmap; } else { bitmap = sentBitmap.copy(sentBitmap.getConfig(), true); } if (radius < 1) { return (null); } int w = bitmap.getWidth(); int h = bitmap.getHeight(); int[] pix = new int[w * h]; bitmap.getPixels(pix, 0, w, 0, 0, w, h); int wm = w - 1; int hm = h - 1; int wh = w * h; int div = radius + radius + 1; int r[] = new int[wh]; int g[] = new int[wh]; int b[] = new int[wh]; int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; int vmin[] = new int[Math.max(w, h)]; int divsum = (div + 1) >> 1; divsum *= divsum; int dv[] = new int[256 * divsum]; for (i = 0; i < 256 * divsum; i++) { dv[i] = (i / divsum); } yw = yi = 0; int[][] stack = new int[div][3]; int stackpointer; int stackstart; int[] sir; int rbs; int r1 = radius + 1; int routsum, goutsum, boutsum; int rinsum, ginsum, binsum; for (y = 0; y < h; y++) { rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; for (i = -radius; i <= radius; i++) { p = pix[yi + Math.min(wm, Math.max(i, 0))]; sir = stack[i + radius]; sir[0] = (p & 0xff0000) >> 16; sir[1] = (p & 0x00ff00) >> 8; sir[2] = (p & 0x0000ff); rbs = r1 - Math.abs(i); rsum += sir[0] * rbs; gsum += sir[1] * rbs; bsum += sir[2] * rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; } } stackpointer = radius; for (x = 0; x < w; x++) { r[yi] = dv[rsum]; g[yi] = dv[gsum]; b[yi] = dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; if (y == 0) { vmin[x] = Math.min(x + radius + 1, wm); } p = pix[yw + vmin[x]]; sir[0] = (p & 0xff0000) >> 16; sir[1] = (p & 0x00ff00) >> 8; sir[2] = (p & 0x0000ff); rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; sir = stack[(stackpointer) % div]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; yi++; } yw += w; } for (x = 0; x < w; x++) { rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; yp = -radius * w; for (i = -radius; i <= radius; i++) { yi = Math.max(0, yp) + x; sir = stack[i + radius]; sir[0] = r[yi]; sir[1] = g[yi]; sir[2] = b[yi]; rbs = r1 - Math.abs(i); rsum += r[yi] * rbs; gsum += g[yi] * rbs; bsum += b[yi] * rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; } if (i < hm) { yp += w; } } yi = x; stackpointer = radius; for (y = 0; y < h; y++) { // Preserve alpha channel: ( 0xff000000 & pix[yi] ) pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; if (x == 0) { vmin[y] = Math.min(y + r1, hm) * w; } p = x + vmin[y]; sir[0] = r[p]; sir[1] = g[p]; sir[2] = b[p]; rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; sir = stack[stackpointer]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; yi += w; } } bitmap.setPixels(pix, 0, w, 0, 0, w, h); return (bitmap); } }DisplayUtil
package com.example.wangxingqi.wangxingqi20180528.utils; import android.content.Context; /** * Created by AchillesL on 2016/11/16. */ public class DisplayUtil { /*手柄起始角度*/ public static final float ROTATION_INIT_NEEDLE = -30; /*截图屏幕宽高*/ private static final float BASE_SCREEN_WIDTH = (float) 1080.0; private static final float BASE_SCREEN_HEIGHT = (float) 1920.0; /*唱针宽高、距离等比例*/ public static final float SCALE_NEEDLE_WIDTH = (float) (276.0 / BASE_SCREEN_WIDTH); public static final float SCALE_NEEDLE_MARGIN_LEFT = (float) (500.0 / BASE_SCREEN_WIDTH); public static final float SCALE_NEEDLE_PIVOT_X = (float) (43.0 / BASE_SCREEN_WIDTH); public static final float SCALE_NEEDLE_PIVOT_Y = (float) (43.0 / BASE_SCREEN_WIDTH); public static final float SCALE_NEEDLE_HEIGHT = (float) (413.0 / BASE_SCREEN_HEIGHT); public static final float SCALE_NEEDLE_MARGIN_TOP = (float) (43.0 / BASE_SCREEN_HEIGHT); /*唱盘比例*/ public static final float SCALE_DISC_SIZE = (float) (813.0 / BASE_SCREEN_WIDTH); public static final float SCALE_DISC_MARGIN_TOP = (float) (190 / BASE_SCREEN_HEIGHT); /*专辑图片比例*/ public static final float SCALE_MUSIC_PIC_SIZE = (float) (533.0 / BASE_SCREEN_WIDTH); /*设备屏幕宽度*/ public static int getScreenWidth(Context context) { return context.getResources().getDisplayMetrics().widthPixels; } /*设备屏幕高度*/ public static int getScreenHeight(Context context) { return context.getResources().getDisplayMetrics().heightPixels; } }