package com.android.app_base.widget; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.CornerPathEffect; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathMeasure; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; import android.view.animation.AnticipateInterpolator; import androidx.annotation.Nullable; import com.android.app_base.R; /** * 播放暂停动效的按钮 */ public final class PlayButton extends View { /** 播放状态 */ public static final int STATE_PLAY = 0; /** 暂停状态 */ public static final int STATE_PAUSE = 1; /** 当前状态 */ private int mCurrentState = STATE_PAUSE; /** 动画时间 */ private int mAnimDuration; private final Paint mPaint; private int mWidth, mHeight; private int mCenterX, mCenterY; private int mCircleRadius; private RectF mRectF, mBgRectF; private float mFraction = 1; private final Path mPath, mDstPath; private final PathMeasure mPathMeasure; private float mPathLength; public PlayButton(Context context) { this(context, null); } public PlayButton(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public PlayButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PlayButton); int lineColor = typedArray.getColor(R.styleable.PlayButton_pb_lineColor, Color.WHITE); int lineSize = typedArray.getInteger(R.styleable.PlayButton_pb_lineSize, (int) getResources().getDimension(R.dimen.dp_4)); mAnimDuration = typedArray.getInteger(R.styleable.PlayButton_pb_animDuration, 200); typedArray.recycle(); // 关闭硬件加速 setLayerType(View.LAYER_TYPE_SOFTWARE, null); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setColor(lineColor); mPaint.setStrokeWidth(lineSize); mPaint.setPathEffect(new CornerPathEffect(1)); mPath = new Path(); mDstPath = new Path(); mPathMeasure = new PathMeasure(); } @Override protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { super.onSizeChanged(width, height, oldWidth, oldHeight); mWidth = width * 9 / 10; mHeight = height * 9 / 10; mCircleRadius = mWidth / (int) getResources().getDimension(R.dimen.dp_4); mCenterX = width / 2; mCenterY = height / 2; mRectF = new RectF(mCenterX - mCircleRadius, mCenterY + 0.6f * mCircleRadius, mCenterX + mCircleRadius, mCenterY + 2.6f * mCircleRadius); mBgRectF = new RectF(mCenterX - mWidth / 2f ,mCenterY - mHeight / 2f , mCenterX + mWidth / 2f, mCenterY + mHeight / 2f); mPath.moveTo(mCenterX - mCircleRadius, mCenterY + 1.8f * mCircleRadius); mPath.lineTo(mCenterX - mCircleRadius, mCenterY - 1.8f * mCircleRadius); mPath.lineTo(mCenterX + mCircleRadius, mCenterY); mPath.close(); mPathMeasure.setPath(mPath, false); mPathLength = mPathMeasure.getLength(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { switch (MeasureSpec.getMode(widthMeasureSpec)) { case MeasureSpec.AT_MOST: case MeasureSpec.UNSPECIFIED: widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) getResources().getDimension(R.dimen.dp_60), MeasureSpec.EXACTLY); break; case MeasureSpec.EXACTLY: default: break; } switch (MeasureSpec.getMode(heightMeasureSpec)) { case MeasureSpec.AT_MOST: case MeasureSpec.UNSPECIFIED: heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) getResources().getDimension(R.dimen.dp_60), MeasureSpec.EXACTLY); break; case MeasureSpec.EXACTLY: default: break; } setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawCircle(mCenterX, mCenterY, mWidth / 2f, mPaint); if (mFraction < 0) { // 弹性部分 canvas.drawLine(mCenterX + mCircleRadius, mCenterY - 1.6f * mCircleRadius + 10 * mCircleRadius * mFraction, mCenterX + mCircleRadius, mCenterY + 1.6f * mCircleRadius + 10 * mCircleRadius * mFraction, mPaint); canvas.drawLine(mCenterX - mCircleRadius, mCenterY - 1.6f * mCircleRadius, mCenterX - mCircleRadius, mCenterY + 1.6f * mCircleRadius, mPaint); canvas.drawArc(mBgRectF, -105 , 360 , false, mPaint); } else if (mFraction <= 0.3) { // 右侧直线和下方曲线 canvas.drawLine(mCenterX + mCircleRadius, mCenterY - 1.6f * mCircleRadius + mCircleRadius * 3.2f / 0.3f * mFraction, mCenterX + mCircleRadius, mCenterY + 1.6f * mCircleRadius, mPaint); canvas.drawLine(mCenterX - mCircleRadius, mCenterY - 1.6f * mCircleRadius, mCenterX - mCircleRadius, mCenterY + 1.6f * mCircleRadius, mPaint); if (mFraction != 0) { canvas.drawArc(mRectF, 0f, 180f / 0.3f * mFraction, false, mPaint); } canvas.drawArc(mBgRectF, -105 + 360 * mFraction, 360 * (1 - mFraction), false, mPaint); } else if (mFraction <= 0.6) { // 下方曲线和三角形 canvas.drawArc(mRectF, 180f / 0.3f * (mFraction - 0.3f), 180 - 180f / 0.3f * (mFraction - 0.3f), false , mPaint); mDstPath.reset(); mPathMeasure.getSegment(0.02f * mPathLength, 0.38f * mPathLength + 0.42f * mPathLength / 0.3f * (mFraction - 0.3f) , mDstPath, true); canvas.drawPath(mDstPath, mPaint); canvas.drawArc(mBgRectF, -105 + 360 * mFraction, 360 * (1 - mFraction), false, mPaint); } else if (mFraction <= 0.8) { // 三角形 mDstPath.reset(); mPathMeasure.getSegment(0.02f * mPathLength + 0.2f * mPathLength / 0.2f * (mFraction - 0.6f) , 0.8f * mPathLength + 0.2f * mPathLength / 0.2f * (mFraction - 0.6f) , mDstPath, true); canvas.drawPath(mDstPath, mPaint); canvas.drawArc(mBgRectF, -105 + 360 * mFraction, 360 * (1 - mFraction), false, mPaint); } else { // 弹性部分 mDstPath.reset(); mPathMeasure.getSegment(10 * mCircleRadius * (mFraction - 1) , mPathLength , mDstPath, true); canvas.drawPath(mDstPath, mPaint); } } /** * 播放状态 */ public void play() { if (mCurrentState == STATE_PLAY) { return; } mCurrentState = STATE_PLAY; ValueAnimator valueAnimator = ValueAnimator.ofFloat(1f, 100f); valueAnimator.setDuration(mAnimDuration); valueAnimator.setInterpolator(new AnticipateInterpolator()); valueAnimator.addUpdateListener(animation -> { mFraction = 1 - animation.getAnimatedFraction(); invalidate(); }); valueAnimator.start(); } /** * 暂停状态 */ public void pause() { if (mCurrentState == STATE_PAUSE) { return; } mCurrentState = STATE_PAUSE; ValueAnimator valueAnimator = ValueAnimator.ofFloat(1.f, 100f); valueAnimator.setDuration(mAnimDuration); valueAnimator.setInterpolator(new AnticipateInterpolator()); valueAnimator.addUpdateListener(animation -> { mFraction = animation.getAnimatedFraction(); invalidate(); }); valueAnimator.start(); } /** * 获取当前状态 */ public int getCurrentState() { return mCurrentState; } /** * 设置动画时间 */ public void setAnimDuration(int duration) { mAnimDuration = duration; } /** * 设置线条颜色 */ public void setLineColor(int color) { mPaint.setColor(color); invalidate(); } /** * 设置线条大小 */ public void setLineSize(int size) { mPaint.setStrokeWidth(size); invalidate(); } }