package me.wcy.music.widget; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.drawable.Drawable; import android.os.Handler; import android.util.AttributeSet; import android.view.View; import android.view.WindowManager; import me.wcy.music.R; import me.wcy.music.utils.CoverLoader; import me.wcy.music.utils.ImageUtils; /** * 专辑封面 * Created by wcy on 2015/11/30. */ public class AlbumCoverView extends View implements ValueAnimator.AnimatorUpdateListener { private static final long TIME_UPDATE = 50L; private static final float DISC_ROTATION_INCREASE = 0.5f; private static final float NEEDLE_ROTATION_PLAY = 0.0f; private static final float NEEDLE_ROTATION_PAUSE = -25.0f; private Handler mHandler = new Handler(); private Bitmap mDiscBitmap; private Bitmap mCoverBitmap; private Bitmap mNeedleBitmap; private Drawable mTopLine; private Drawable mCoverBorder; private int mTopLineHeight; private int mCoverBorderWidth; private Matrix mDiscMatrix = new Matrix(); private Matrix mCoverMatrix = new Matrix(); private Matrix mNeedleMatrix = new Matrix(); private ValueAnimator mPlayAnimator; private ValueAnimator mPauseAnimator; private float mDiscRotation = 0.0f; private float mNeedleRotation = NEEDLE_ROTATION_PLAY; private boolean isPlaying = false; // 图片起始坐标 private Point mDiscPoint = new Point(); private Point mCoverPoint = new Point(); private Point mNeedlePoint = new Point(); // 旋转中心坐标 private Point mDiscCenterPoint = new Point(); private Point mCoverCenterPoint = new Point(); private Point mNeedleCenterPoint = new Point(); public AlbumCoverView(Context context) { this(context, null); } public AlbumCoverView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public AlbumCoverView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mTopLine = getResources().getDrawable(R.drawable.play_page_cover_top_line_shape); mCoverBorder = getResources().getDrawable(R.drawable.play_page_cover_border_shape); mDiscBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.play_page_disc); mDiscBitmap = ImageUtils.resizeImage(mDiscBitmap, (int) (getScreenWidth() * 0.75), (int) (getScreenWidth() * 0.75)); mCoverBitmap = CoverLoader.getInstance().loadRound(null); mNeedleBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.play_page_needle); mNeedleBitmap = ImageUtils.resizeImage(mNeedleBitmap, (int) (getScreenWidth() * 0.25), (int) (getScreenWidth() * 0.375)); mTopLineHeight = dp2px(1); mCoverBorderWidth = dp2px(1); mPlayAnimator = ValueAnimator.ofFloat(NEEDLE_ROTATION_PAUSE, NEEDLE_ROTATION_PLAY); mPlayAnimator.setDuration(300); mPlayAnimator.addUpdateListener(this); mPauseAnimator = ValueAnimator.ofFloat(NEEDLE_ROTATION_PLAY, NEEDLE_ROTATION_PAUSE); mPauseAnimator.setDuration(300); mPauseAnimator.addUpdateListener(this); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); initSize(); } /** * 确定图片起始坐标与旋转中心坐标 */ private void initSize() { int discOffsetY = mNeedleBitmap.getHeight() / 2; mDiscPoint.x = (getWidth() - mDiscBitmap.getWidth()) / 2; mDiscPoint.y = discOffsetY; mCoverPoint.x = (getWidth() - mCoverBitmap.getWidth()) / 2; mCoverPoint.y = discOffsetY + (mDiscBitmap.getHeight() - mCoverBitmap.getHeight()) / 2; mNeedlePoint.x = getWidth() / 2 - mNeedleBitmap.getWidth() / 6; mNeedlePoint.y = -mNeedleBitmap.getWidth() / 6; mDiscCenterPoint.x = getWidth() / 2; mDiscCenterPoint.y = mDiscBitmap.getHeight() / 2 + discOffsetY; mCoverCenterPoint.x = mDiscCenterPoint.x; mCoverCenterPoint.y = mDiscCenterPoint.y; mNeedleCenterPoint.x = mDiscCenterPoint.x; mNeedleCenterPoint.y = 0; } @Override protected void onDraw(Canvas canvas) { // 1.绘制顶部虚线 mTopLine.setBounds(0, 0, getWidth(), mTopLineHeight); mTopLine.draw(canvas); // 2.绘制黑胶唱片外侧半透明边框 mCoverBorder.setBounds(mDiscPoint.x - mCoverBorderWidth, mDiscPoint.y - mCoverBorderWidth, mDiscPoint.x + mDiscBitmap.getWidth() + mCoverBorderWidth, mDiscPoint.y + mDiscBitmap.getHeight() + mCoverBorderWidth); mCoverBorder.draw(canvas); // 3.绘制黑胶 // 设置旋转中心和旋转角度,setRotate和preTranslate顺序很重要 mDiscMatrix.setRotate(mDiscRotation, mDiscCenterPoint.x, mDiscCenterPoint.y); // 设置图片起始坐标 mDiscMatrix.preTranslate(mDiscPoint.x, mDiscPoint.y); canvas.drawBitmap(mDiscBitmap, mDiscMatrix, null); // 4.绘制封面 mCoverMatrix.setRotate(mDiscRotation, mCoverCenterPoint.x, mCoverCenterPoint.y); mCoverMatrix.preTranslate(mCoverPoint.x, mCoverPoint.y); canvas.drawBitmap(mCoverBitmap, mCoverMatrix, null); // 5.绘制指针 mNeedleMatrix.setRotate(mNeedleRotation, mNeedleCenterPoint.x, mNeedleCenterPoint.y); mNeedleMatrix.preTranslate(mNeedlePoint.x, mNeedlePoint.y); canvas.drawBitmap(mNeedleBitmap, mNeedleMatrix, null); } public void initNeedle(boolean isPlaying) { mNeedleRotation = isPlaying ? NEEDLE_ROTATION_PLAY : NEEDLE_ROTATION_PAUSE; invalidate(); } public void setCoverBitmap(Bitmap bitmap) { mCoverBitmap = bitmap; mDiscRotation = 0.0f; invalidate(); } public void start() { if (isPlaying) { return; } isPlaying = true; mHandler.post(mRotationRunnable); mPlayAnimator.start(); } public void pause() { if (!isPlaying) { return; } isPlaying = false; mHandler.removeCallbacks(mRotationRunnable); mPauseAnimator.start(); } @Override public void onAnimationUpdate(ValueAnimator animation) { mNeedleRotation = (float) animation.getAnimatedValue(); invalidate(); } private Runnable mRotationRunnable = new Runnable() { @Override public void run() { if (isPlaying) { mDiscRotation += DISC_ROTATION_INCREASE; if (mDiscRotation >= 360) { mDiscRotation = 0; } invalidate(); } mHandler.postDelayed(this, TIME_UPDATE); } }; private int getScreenWidth() { WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); return wm.getDefaultDisplay().getWidth(); } private int dp2px(float dpValue) { float scale = getContext().getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } }