仿360市场下载按钮

来源:互联网 发布:陕西网络在线发票系统 编辑:程序博客网 时间:2024/04/27 18:27


先看一下效果:

无论多复杂的动画我们都是可以分割成小单元的,然后分步来实现。这个动画大概分为收缩,准备,加载,完成几个部分。为此定义一个枚举类来描述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。