Android仿360市场下载按钮的实现方法

首先来看看效果图:

无论多复杂的动画我们都是可以分割成小单元的,然后分步来实现。这个动画大概分为收缩,准备,加载,完成几个部分。为此定义一个枚举类来描述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,right,mHeight),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,mBgPaint);
 canvas.save();
 mTextPaint.setStyle(Paint.Style.FILL);
 canvas.rotate(mAngle,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,mTextPaint);
 //右下小圆参数
 cXX = (float) (cX + Math.sqrt(2) / 2f * (radius + rr / 3f));
 canvas.drawCircle(cXX,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,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,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,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,progressRight,mHeight);
 canvas.drawRoundRect(new RectF(left,mBgPaint);
 canvas.restore();

 if (mProgress != 100) {
 canvas.drawCircle(mWidth - 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 + "%",mTextPaint);
 }

项目主页:http://www.open-open.com/lib/view/home/1494985743108

本地下载:http://xiazai.jb51.net/201705/yuanma/Metal626-360DownLoadView(jb51.net).rar

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对编程小技巧的支持。

相关文章

AdvserView.java package com.earen.viewflipper; import an...
ImageView的scaleType的属性有好几种,分别是matrix(默认)...
文章浏览阅读8.8k次,点赞9次,收藏20次。本文操作环境:win1...
文章浏览阅读1.2w次,点赞15次,收藏69次。实现目的:由main...
文章浏览阅读3.8w次。前言:最近在找Android上的全局代理软件...
文章浏览阅读2.5w次,点赞17次,收藏6次。创建项目后,运行项...