仿360市场下载按钮

作者: 轻云时解被占用了 | 来源:发表于2017-05-16 15:14 被阅读143次

    先看一下效果:


    无论多复杂的动画我们都是可以分割成小单元的,然后分步来实现。这个动画大概分为收缩,准备,加载,完成几个部分。为此定义一个枚举类来描述view的状态。
    public enum Status { NORMAL, START, PRE, EXPAND, LOAD, END }

    收缩动画

    使用动画不断改变圆角矩形的宽度,触发重绘。代码如下:

    private void initAnim() {
            Animation animation1 = new Animation() {
                @Override
                protected void applyTransformation(float interpolatedTime, Transformation t) {
                    mCurrLength = mWidth * (1 - interpolatedTime);
                    if (mCurrLength < mHeight) {
                        mCurrLength = mHeight;
                        clearAnimation();
                        mAngleAnim.start();
                    }
                    invalidate();
                }
            };
    
            animation1.setDuration(mShrinkDuration);
            animation1.setInterpolator(new LinearInterpolator());
            animation1.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                    mStatus = Status.START;
                }
    
                @Override
                public void onAnimationEnd(Animation animation) {
    
                }
    
                @Override
                public void onAnimationRepeat(Animation animation) {
    
                }
            });
            mShrinkAnim = animation1;
            ...
    }       
    

    onDraw中绘制:

            if (mStatus == Status.START || mStatus == Status.NORMAL) {
                float left = (mWidth - mCurrLength) / 2f;
                float right = (mWidth + mCurrLength) / 2f;
                float r = mHeight / 2f;
                canvas.drawRoundRect(new RectF(left, 0, right, mHeight), r, r, mBgPaint);
                if (mStatus == Status.NORMAL) {
                    Paint.FontMetrics fm = mTextPaint.getFontMetrics();
                    float y = mHeight / 2 + (fm.descent - fm.ascent) / 2 - fm.descent;
                    canvas.drawText("下载", mWidth / 2, y, mTextPaint);
                }
            }
    

    准备动画

    此时旋转动画,是通过canvas绘制背景圆和三个小圆,然后不断旋转画布来实现的,具体求圆心坐标和角度动画我们直接看代码:

     ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mAngle += mPreAnimSpeed;
                    invalidate();
                }
            });
            animator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {
                    mStatus = Status.PRE;
                }
    
                @Override
                public void onAnimationEnd(Animator animation) {
                    mAngleAnim.cancel();
                    startAnimation(mTranslateAnim);
                }
    
                @Override
                public void onAnimationCancel(Animator animation) {
    
                }
    
                @Override
                public void onAnimationRepeat(Animator animation) {
    
                }
            });
    
            animator.setDuration(mPreAnimDuration);
            animator.setInterpolator(new LinearInterpolator());
            mAngleAnim = animator;
    

    onDraw中绘制代码:

      if (mStatus == Status.PRE) {
                canvas.drawCircle(mWidth / 2f, mHeight / 2f, mHeight / 2f, mBgPaint);
                canvas.save();
                mTextPaint.setStyle(Paint.Style.FILL);
                canvas.rotate(mAngle, mWidth / 2, mHeight / 2);
                //大圆的圆心 半径
                float cX = mWidth / 2f;
                float cY = mHeight / 2f;
                float radius = mHeight / 2 / 3f;
                canvas.drawCircle(cX, cY, radius, mTextPaint);
                //上方小圆的参数
                float rr = radius / 2f;
                float cYY = mHeight / 2 - (radius + rr / 3);
                canvas.drawCircle(cX, cYY, rr, mTextPaint);
                //左下小圆参数
                float cXX = (float) (cX - Math.sqrt(2) / 2f * (radius + rr / 3f));
                cYY = (float) (mHeight / 2 + Math.sqrt(2) / 2f * (radius + rr / 3f));
                canvas.drawCircle(cXX, cYY, rr, mTextPaint);
                //右下小圆参数
                cXX = (float) (cX + Math.sqrt(2) / 2f * (radius + rr / 3f));
                canvas.drawCircle(cXX, cYY, rr, mTextPaint);
                canvas.restore();
            }
    

    展开动画

    展开动画也是不断改变view的宽度并重绘圆角矩形,同时需要对准备动画的状态进行向右位移。

     Animation animator1 = new Animation() {
                @Override
                protected void applyTransformation(float interpolatedTime, Transformation t) {
                    mCurrLength = mHeight + (mWidth - mHeight) * interpolatedTime;
                    mTranslationX = (mWidth - mHeight) / 2 * interpolatedTime;
                    invalidate();
                }
            };
            animator1.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                    mStatus = Status.EXPAND;
                }
    
                @Override
                public void onAnimationEnd(Animation animation) {
                    clearAnimation();
                    mLoadAngleAnim.start();
                    mMovePointAnim.start();
                }
    
                @Override
                public void onAnimationRepeat(Animation animation) {
    
                }
            });
            animator1.setDuration(mExpandAnimDuration);
            animator1.setInterpolator(new LinearInterpolator());
            mTranslateAnim = animator1;
    

    onDraw中绘制代码

      if (mStatus == Status.EXPAND) {
                float left = (mWidth - mCurrLength) / 2f;
                float right = (mWidth + mCurrLength) / 2f;
                float r = mHeight / 2f;
                canvas.drawRoundRect(new RectF(left, 0, right, mHeight), r, r, mBgPaint);
                canvas.save();
                mTextPaint.setStyle(Paint.Style.FILL);
                canvas.translate(mTranslationX, 0);
                //大圆的圆心 半径
                float cX = mWidth / 2f;
                float cY = mHeight / 2f;
                float radius = mHeight / 2 / 3f;
                canvas.drawCircle(cX, cY, radius, mTextPaint);
                //上方小圆的参数
                float rr = radius / 2f;
                float cYY = mHeight / 2 - (radius + rr / 3);
                canvas.drawCircle(cX, cYY, rr, mTextPaint);
                //左下小圆参数
                float cXX = (float) (cX - Math.sqrt(2) / 2f * (radius + rr / 3f));
                cYY = (float) (mHeight / 2 + Math.sqrt(2) / 2f * (radius + rr / 3f));
                canvas.drawCircle(cXX, cYY, rr, mTextPaint);
                //右下小圆参数
                cXX = (float) (cX + Math.sqrt(2) / 2f * (radius + rr / 3f));
                canvas.drawCircle(cXX, cYY, rr, mTextPaint);
                canvas.restore();
            }
    

    加载动画

    加载动画分三部分,右侧的旋转动画,正弦轨迹运动的小球动画,进度更新的动画。正弦动画要求出正弦函数的周期,y轴偏移量,x轴偏移量。

     ValueAnimator animator2 = ValueAnimator.ofFloat(0, 1);
            animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mLoadAngle += mLoadRotateAnimSpeed;
                    invalidate();
                }
            });
            animator2.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {
                    mStatus = Status.LOAD;
                }
    
                @Override
                public void onAnimationEnd(Animator animation) {
                    mLoadAngleAnim.cancel();
                }
    
                @Override
                public void onAnimationCancel(Animator animation) {
    
                }
    
                @Override
                public void onAnimationRepeat(Animator animation) {
    
                }
            });
            animator2.setDuration(Integer.MAX_VALUE);
            animator2.setInterpolator(new LinearInterpolator());
            mLoadAngleAnim = animator2;
    

    onDraw中绘制代码:

     if (mStatus == Status.LOAD || mStatus == Status.END) {
                float left = (mWidth - mCurrLength) / 2f;
                float right = (mWidth + mCurrLength) / 2f;
                float r = mHeight / 2f;
                mBgPaint.setColor(mProgressColor);
                canvas.drawRoundRect(new RectF(left, 0, right, mHeight), r, r, mBgPaint);
                if (mProgress != 100) {
                    for (int i = 0; i < mFourMovePoints.length; i++) {
                        if (mFourMovePoints[i].isDraw)
                            canvas.drawCircle(mFourMovePoints[i].moveX, mFourMovePoints[i].moveY, mFourMovePoints[i].radius, mTextPaint);
                    }
                }
                float progressRight = mProgress * mWidth / 100f;
                mBgPaint.setColor(mBgColor);
                canvas.save();
                canvas.clipRect(0, 0, progressRight, mHeight);
                canvas.drawRoundRect(new RectF(left, 0, right, mHeight), r, r, mBgPaint);
                canvas.restore();
    
                if (mProgress != 100) {
                    canvas.drawCircle(mWidth - mHeight / 2, mHeight / 2, mHeight / 2, mBgPaint);
                    canvas.save();
                    mTextPaint.setStyle(Paint.Style.FILL);
                    canvas.rotate(mLoadAngle, mWidth - mHeight / 2, mHeight / 2);
                    canvas.drawCircle(mWidth - mHeight + 30, getCenterY(mWidth - mHeight + 30, 5), 5, mTextPaint);
                    canvas.drawCircle(mWidth - mHeight + 45, getCenterY(mWidth - mHeight + 45, 8), 8, mTextPaint);
                    canvas.drawCircle(mWidth - mHeight + 68, getCenterY(mWidth - mHeight + 68, 11), 11, mTextPaint);
                    canvas.drawCircle(mWidth - mHeight + 98, getCenterY(mWidth - mHeight + 98, 14), 14, mTextPaint);
                    canvas.restore();
                }
    
                Paint.FontMetrics fm = mTextPaint.getFontMetrics();
                float y = mHeight / 2 + (fm.descent - fm.ascent) / 2 - fm.descent;
                canvas.drawText(mProgress + "%", mWidth / 2, y, mTextPaint);
            }
    

    完整代码见GitHub

    相关文章

      网友评论

      本文标题:仿360市场下载按钮

      本文链接:https://www.haomeiwen.com/subject/mwylxxtx.html