仿 360 市場下載按鈕

anio123 7年前發布 | 16K 次閱讀 安卓開發 Android開發 移動開發

效果圖

無論多復雜的動畫我們都是可以分割成小單元的,然后分步來實現。這個動畫大概分為收縮,準備,加載,完成幾個部分。為此定義一個枚舉類來描述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);
        }

項目主頁:http://www.baiduhome.net/lib/view/home/1494985743108

 

 本文由用戶 anio123 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!