仿 360 市场下载按钮

anio123 8年前
   <p style="text-align:center"><img src="https://simg.open-open.com/show/0b34b77a4520014d1433b302591f0c1a.gif"></p>    <p style="text-align:center">效果图</p>    <p>无论多复杂的动画我们都是可以分割成小单元的,然后分步来实现。这个动画大概分为收缩,准备,加载,完成几个部分。为此定义一个枚举类来描述view的状态。</p>    <p>public enum Status { NORMAL, START, PRE, EXPAND, LOAD, END }</p>    <h2>收缩动画</h2>    <p>使用动画不断改变圆角矩形的宽度,触发重绘。代码如下:</p>    <pre>  <code class="language-java">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;          ...  }</code></pre>    <p>onDraw中绘制:</p>    <pre>  <code class="language-java">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);              }          }</code></pre>    <h2>准备动画</h2>    <p>此时旋转动画,是通过canvas绘制背景圆和三个小圆,然后不断旋转画布来实现的,具体求圆心坐标和角度动画我们直接看代码:</p>    <pre>  <code class="language-java">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;</code></pre>    <p>onDraw中绘制代码:</p>    <pre>  <code class="language-java">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();          }</code></pre>    <h2>展开动画</h2>    <p>展开动画也是不断改变view的宽度并重绘圆角矩形,同时需要对准备动画的状态进行向右位移。</p>    <pre>  <code class="language-java">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;</code></pre>    <p>onDraw中绘制代码</p>    <pre>  <code class="language-java">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();          }</code></pre>    <h2>加载动画</h2>    <p>加载动画分三部分,右侧的旋转动画,正弦轨迹运动的小球动画,进度更新的动画。正弦动画要求出正弦函数的周期,y轴偏移量,x轴偏移量。</p>    <pre>  <code class="language-java">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;</code></pre>    <p>onDraw中绘制代码:</p>    <pre>  <code class="language-java">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);          }</code></pre>    <p>项目主页:<a href="http://www.open-open.com/lib/view/home/1494985743108">http://www.open-open.com/lib/view/home/1494985743108</a></p>    <p> </p>