高仿QQ运动的周报界面

这次高仿的是QQ运动的周报界面的网图。这个控件刚开始的时候以为代码量不大,没想到一路下来界面代码在加上动画代码还是蛮多的。好了老规矩先上图:


效果图.gif

效果还是和qq的才不多吧。 1. 首先我把各个变量都贴出来以便在后续中你们可以更好理解代码的意思:

//屏幕的宽度 
private int mScreemWidth; 
//屏幕的高度 
private int mScreemHight;
 //圆的线 
private Paint mCirclePaint; 
//圆区域的颜色 
private Paint mCirclePaintColor; 
//虚线 
private Paint mLineCircle;
 //圆点 
private Paint mCircleHoldPaint; 
//画字体 
private Paint mCenterCircle; 
//最外的圆的透明度 
private int mCircleAlpha1=0; 
//中间的圆的透明度 
private int mCircleAlpha2=0; 
//最内的圆的透明度 
private int mCircleAlpha3=0; 
//好友排名 
private int mFriendDranking=0; 
//达标天数 
private int mStandardDay=0; 
//平均步数 
private int mAverageCount=0; 
//好友排名的X轴坐标 
private float mFriendDrankingX=0; 
//好友排名的Y轴坐标 
private float mFriendDrankingY=0; 
//平均步数的X轴坐标
private float mStandardDayX=0; 
//平均步数的Y轴坐标 
private float mStandardDayY=0;
 //达标天数的X轴坐标 
private float mAverageCountX=0; 
//达标天数的Y轴坐标 
private float mAverageCountY=0; 
//临时的View的半径 
private int tempCircleRadius=0; 
//View的半径 
private int circleRadius=0; 
//每个圆圈的间隔 
private float marginCircleSize=0; 
//圆的颜色 
private int circleColor=0; 
//朋友区域的颜色 
private int friendColor; 
//平均步数区域的颜色 
private int averageColor; 
//达标天数区域的颜色 
private int standardColor; 
//总步数 
private String allStep; 
//好友排名
private String firendDrank; 
//达标天数 
private String standarDay; 
//平均步数 
private String averageCount; 
//波浪动画的数值 
private int waveData=-30; 
//中间文字翻转动画的数值 
private float centerData=0; 
//画波浪的看门狗 
private boolean waveWatchDag=false; 
//画虚线的看门狗 
private boolean lineWatchDag=false; 
//各点解释的看门狗 
private boolean expainWatchDag=false; 
//中心圆的内容的看门狗 
private boolean centerWatchDag=false; 
//解释的字符串 
private String averageCountTxt="平均步数"; 
private String friendDrankTxt="好友排名"; 
private String standarDayTxt="达标天数"; 
private String theyCount="本周总步数"; 
private String tip="步";

2.有点多了,其次就是测量View的大小的onMeasure():

@Override protected void onMeasure(
int widthMeasureSpec, int heightMeasureSpec) {
 int widthModel=MeasureSpec.getMode(widthMeasureSpec); 
 int heightModel=MeasureSpec.getMode(heightMeasureSpec); 
 int measureWidth=MeasureSpec.getSize(widthMeasureSpec); 
 int measureHeight=MeasureSpec.getSize(heightMeasureSpec); 
 int width; 
 int height; 
   if(widthModel==MeasureSpec.EXACTLY){ 
     width=measureWidth; 
   }else{ 
    width=getPaddingLeft()+getPaddingRight()+measureWidth;
   }
   if(heightModel==MeasureSpec.EXACTLY){ 
    height=measureHeight; 
   }else{ 
    height=(getPaddingLeft()+getPaddingRight()+measureHeight)/2; 
  } 
   setMeasuredDimension(width,height); 
  loadAnimator(); 
}

3.这里当设置大小为wrap_content的时候,View的宽度的话是用屏幕的的宽,而View的高的话是屏幕的高度的一半。当View的大小生成之后会调用onSizeChange()方法,具体操作如下:

@Override 
protected void onSizeChanged(int w, int h, int oldw, int oldh) { 
super.onSizeChanged(w, h, oldw, oldh); 
mScreemWidth=w; 
mScreemHight=h;
  //得出最大的圆的半径 
  if(mScreemWidth>mScreemHight){ 
     circleRadius=Float.valueOf((w/3.4)+"").intValue(); 
  }else{ 
    circleRadius=Float.valueOf((h/3.4)+"").intValue(); 
  } 
 if(tempCircleRadius!=0&&tempCircleRadius<=circleRadius){          
    circleRadius=tempCircleRadius; 
 } 
   //得出每个圆的间隔 
   marginCircleSize=circleRadius/6; 
}

当View的宽度大于View的高度时,最外边的圆的半径就是w/3.4,反之当View的高度大于View的宽度时,最外边的圆的半径就是h/3.4,而每个圆的间隔就是圆半径的六分之一。4.接着就是最重要的onDraw()方法了,代码如下:

@Override protected void onDraw(Canvas canvas) { 
canvas.translate(getWidth()/2,getHeight()/2-(circleRadius/6)); 
  canvas.save(); 
  //画出三条圆圈 
  drawCircle(canvas);
  //画出波浪图形 
  drawWaves(canvas); 
  //画虚线 
  drawDottedLine(canvas); 
  //画点 
  drawCircleHold(canvas); 
  //画解释的内容 
  drawExpain(canvas); 
  //画中心圆的内容 
  centerCircleContent(canvas); 
}

首先把canvas的原点移到X轴为宽度的一半,Y轴为高度的一半再减去六分之一的半径,保存canvas的状态。接着就一个个说这里面的每一个方法:

//画出三条圆圈 
public void drawCircle(Canvas canvas){ 
  //画出最大的圆 
  mCirclePaint.setAlpha(mCircleAlpha1);
  canvas.drawCircle(0,0,circleRadius,mCirclePaint); 
  //画出第二大的圆 
  mCirclePaint.setAlpha(mCircleAlpha2); 
  canvas.drawCircle(0,0,circleRadius-marginCircleSize,mCirclePaint); 
  mCirclePaintColor.setColor(Color.parseColor("#F1FCFE")); 
  mCirclePaintColor.setAlpha(mCircleAlpha2); 
  canvas.drawCircle(0,0,circleRadius-marginCircleSize-2,mCirclePaintColor); 
  //画出第三大的圆 
  mCirclePaint.setAlpha(mCircleAlpha3); 
  canvas.drawCircle(0,0,circleRadius-marginCircleSize*2,mCirclePaint); 
  mCirclePaintColor.setColor(Color.parseColor("#E7F9FE")); 
  mCirclePaintColor.setAlpha(mCircleAlpha3); 
  canvas.drawCircle(0,0,circleRadius-marginCircleSize*2-2,mCirclePaintColor); 
}

这个方法是比较简单的,就是画出三个圆圈,每个圆圈的间隔就是前面所初始化的marginCircleSize,圆圈的圆心就是canvas的原点,之前我们移动过原点了。第二和第三个圆圈里面还配有圆的背景,效果如下图:


img1.PNG

接着就是画出波浪图形的方法 drawWaves(canvas)代码如下:

 //画出波浪图形 
public void drawWaves(Canvas canvas){
 if(!waveWatchDag){
     return ;
 } 

canvas.rotate(waveData);
float inCircleRadius=circleRadius-marginCircleSize*3; 
//算出最上面的点 
float topPointX=0; 
float topPointY=-inCircleRadius; 
//算出左下角的点 
float leftBottpmPointX=-(float)Math.sqrt(Math.pow(inCircleRadius,2)-Math.pow(inCircleRadius/2,2));
float leftBottomPointY=inCircleRadius/2; 
//算出右小角的点 
float rightBottomPointX=-leftBottpmPointX;
float rightBottomPointY=inCircleRadius/2;
//得到好友排名半径
float mFriendDrankingData=circleValue(mFriendDranking); 
//得到达标天数半径 
float mStandarDayData=circleValue(mStandardDay); 
//得到平均步数半径
float mAverageCountData=circleValue(mAverageCount); 

/*画好友排名*/ 
//得出左上角的圆的坐标
float[] mFriendDrankingPoint=calculatePoint(mFriendDrankingData); 
//好友排名的X轴坐标 
mFriendDrankingX=-mFriendDrankingPoint[0]; 
//好友排名的Y轴坐标 
mFriendDrankingY=-mFriendDrankingPoint[1]; 
//画出还有排名的波浪线 
Path mFriendDrankingPath=new Path(); 
mFriendDrankingPath.moveTo(leftBottpmPointX,leftBottomPointY); 
mFriendDrankingPath.lineTo(mFriendDrankingX-6,mFriendDrankingY-6); 
mFriendDrankingPath.lineTo(topPointX,topPointY); 
mFriendDrankingPath.lineTo(topPointX+10,topPointY+10); 
mCirclePaintColor.setPathEffect(new CornerPathEffect(20)); 
mCirclePaintColor.setColor(friendColor); 
canvas.drawPath(mFriendDrankingPath,mCirclePaintColor);

 /*画达标天数*/ 
//得出右上角的圆的坐标
 float[] mStandarDayPoint=calculatePoint(mStandarDayData);
 //达标天数的X轴坐标
 mStandardDayX=mStandarDayPoint[0]; 
//达标天数的Y轴坐标 
mStandardDayY=-mStandarDayPoint[1];
 //画出还有达标天数的波浪线 
Path mStandarDayPath=new Path();
 mStandarDayPath.moveTo(topPointX,topPointY); 
mStandarDayPath.lineTo(mStandardDayX+6,mStandardDayY-6); 
mStandarDayPath.lineTo(rightBottomPointX,rightBottomPointY); 
mStandarDayPath.lineTo(rightBottomPointX-10,rightBottomPointY+10); 
mCirclePaintColor.setColor(standardColor); 
canvas.drawPath(mStandarDayPath,mCirclePaintColor); 

/*平均步数*/
//平均步数的X轴坐标 
mAverageCountX=0; 
//平均步数的Y轴坐标 
mAverageCountY=mAverageCountData;
//画出还有平均步数的波浪线 
Path mAverageCountPath=new Path();
mAverageCountPath.moveTo(rightBottomPointX,rightBottomPointY); 
mAverageCountPath.lineTo(topPointX,mAverageCountData+8); 
mAverageCountPath.lineTo(leftBottpmPointX,leftBottomPointY); 
mAverageCountPath.lineTo(leftBottpmPointX+10,leftBottomPointY+10); 
mCirclePaintColor.setColor(averageColor); 
canvas.drawPath(mAverageCountPath,mCirclePaintColor);
 //最里面的圆 mCirclePaintColor.setColor(Color.WHITE); 
canvas.drawCircle(0,0,circleRadius-marginCircleSize*3,mCirclePaintColor); 
}

这方法里最核心的就是数学计算了,整个View有3个波浪区域,各占一个圆的三分之一,所以第一步就是计算出这个圆的左下角,右小角和正上角的三个点,如图


img2.PNG

的蓝色点所示。具体代码见注释。在通过circleValue算出波浪线的半径:

 //算出弧线区域的半径 
public float circleValue(int mDataDranking){ 
if(mDataDranking==1){ 
    return circleRadius-marginCircleSize*2; 
}else if(mDataDranking==2){ 
    return circleRadius-marginCircleSize; 
}else if(mDataDranking==3){ 
    return circleRadius; 
}else{ 
    return circleRadius-marginCircleSize*2;
 } }

然后通过calculatePoint()方法来各个波浪区域对应的顶点,代码如下:

//算出右上角或左上角的坐标 
public float[] calculatePoint(float radius){ 
   float[] result=new float[2]; 
   float pointY=radius/2; 
   float pointX=(float)Math.sqrt(Math.pow(radius,2)-Math.pow(pointY,2)); 
   result[0]=pointX; 
   result[1]=pointY; 
   return result; 
}

最后转化为形象的图就是:


img3.PNG

接着用Path把各个区域的点连起来就是形成区域,不过现在还是尖角,要把它变成原角就要用mCirclePaintColor.setPathEffect(new CornerPathEffect(20));方法,这样各个边的连接处都可以转换成圆角,可是因为是圆角所以到不到圆圈的边,这时候你要对你的顶点进行微调,所以我再顶点都进行了减6或者加6的操作。至于我这个6是怎么得出来的,我用的等比例的数学方法来求出来的,到时有优化我可以把我的方法用代码表示出来。至此,重要的就说完了,剩下的只是用canvas和path和paint画出来就是了。效果如下:


img4.PNG

接着就是画虚线的方法了drawDottedLine(canvas)代码如下:
 //画圆点和虚线 
public void drawDottedLine(Canvas canvas){ 
    if(!lineWatchDag){ 
       return; 
    } 
   for(int i=0;i<3;i++){ 
      canvas.rotate(120); 
    if(i==0){ 
    //画好友排名的虚线
    mLineCircle.setTextSize(18); 
    mLineCircle.setColor(friendColor);     
   drawDottedLine(canvas,judgeDotte(mFriendDranking)); 
    }else if(i==1){ 
   //画达标天数的虚线 
   mLineCircle.setColor(standardColor); 
   drawDottedLine(canvas,judgeDotte(mStandardDay));
   }else if(i==2){ 
   //画平均步数的虚线 
  mLineCircle.setColor(averageColor); 
  drawDottedLine(canvas,judgeDotte(mAverageCount)); 
} } 
  canvas.restore(); 
}  

//判断虚线 
public List<Float> judgeDotte(int value){ List<Float> temp=new ArrayList<>(); 
     if(value==1){ 
       //当为1时,波浪顶点到第三个圆  
      temp.add(circleRadius-marginCircleSize*2); 
      temp.add((float)circleRadius); 
      temp.add(circleRadius-marginCircleSize*3); 
    }else if(value==2){ 
     //当为2时,波浪顶点到第二个圆 
     temp.add(circleRadius-marginCircleSize); 
     temp.add((float)circleRadius); 
     temp.add(circleRadius-marginCircleSize*3); 
   }else if(value==3){ 
   //当为3时,波浪顶点到第一个圆
    temp.add(circleRadius-marginCircleSize*3);
    temp.add((float)circleRadius); 
  } 
   return temp; 
} 
//画虚线 
public void drawDottedLine(Canvas canvas,List<Float> data){ 
    if(data.size()==2){
       /*当数值是最大的是时候也就是3*/
      mLineCircle.setColor(Color.WHITE);
      Path path=new Path(); 
     path.moveTo(0,data.get(0)); 
     path.lineTo(0,data.get(1));
     canvas.drawPath(path,mLineCircle);
     return ; 
}else{ 
    /*当数值在1和2的时候*/ 
   //画出数值外的虚线 
   Path pathOut=new Path(); 
   pathOut.moveTo(0,data.get(0));
   pathOut.lineTo(0,data.get(1));
   mLineCircle.setPathEffect(new DashPathEffect(new float[]{7,5,7,5},5)); 
  canvas.drawPath(pathOut,mLineCircle); 
  //画出数值内的虚线 
  Path pathIn=new Path(); 
  pathIn.moveTo(0,data.get(1)); 
  pathIn.lineTo(0,data.get(2)); 
  mLineCircle.setColor(Color.WHITE); 
  canvas.drawPath(pathIn,mLineCircle); 
} 
}

首先canvas通过每次旋转120度来画出每一条波浪线,通过judgeDotte()方法得出波浪线三个点对应的Y轴的坐标,假如judgeDotte返回的个数是两个的话那就是证明顶点在最外面的圆,假如是3个的话就画出顶点之外和顶点之内的线就可以了,代码注释已经很详细了,效果图如下:


img5.PNG

接着是画虚线上的圆点,drawCircleHold(Canvas canvas)代码如下:

//画虚线上的圆点 
public void drawCircleHold(Canvas canvas){ 
  if(!lineWatchDag){
     return;
  }
 float[] yuan1=calculatePoint(circleRadius-marginCircleSize*2); 
 float[] yuan2=calculatePoint(circleRadius-marginCircleSize); 
 float[] yuan3=calculatePoint(circleRadius); 
//画好友排名的圆点 
drawCircleHoldImpl(-yuan1[0],-yuan1[1],-yuan2[0],-yuan2[1], -yuan3[0],-yuan3[1],mFriendDranking,canvas,friendColor);
 //画达标天数的圆点 
drawCircleHoldImpl(yuan1[0],-yuan1[1],yuan2[0],-yuan2[1], yuan3[0],-yuan3[1],mStandardDay,canvas,standardColor);
 //画平均步数的圆点 drawCircleHoldImpl(0,circleRadius-marginCircleSize*2,0,circleRadius-marginCircleSize, 0,circleRadius,mAverageCount,canvas,averageColor); 
expainWatchDag=true;
 } 
//画圆的具体的方法 
public void drawCircleHoldImpl(float mCirlce1X,float mCircle1Y,float mCirlce2X,float mCircle2Y, float mCirlce3X,float mCircle3Y,int action,Canvas canvas,int color){ 
mCircleHoldPaint.setColor(color); 
if(action==1){ 
//当数值为3时画所有圆圈 canvas.drawCircle(mCirlce1X,mCircle1Y,8,mCircleHoldPaint); 
canvas.drawCircle(mCirlce2X,mCircle2Y,8,mCircleHoldPaint); 
}else if(action==2){
 //当数值为2时画中间的圆圈 
canvas.drawCircle(mCirlce2X,mCircle2Y,8,mCircleHoldPaint);
 } 
//画一定要画的圆圈和圆点 
canvas.drawCircle(mCirlce3X,mCircle3Y,8,mCircleHoldPaint); 
mCircleHoldPaint.setColor(Color.WHITE); 
canvas.drawCircle(mCirlce1X,mCircle1Y,6,mCircleHoldPaint); 
canvas.drawCircle(mCirlce2X,mCircle2Y,6,mCircleHoldPaint); 
canvas.drawCircle(mCirlce3X,mCircle3Y,6,mCircleHoldPaint); 
}

这里同样注释也是很详细的,整个思路就是通过calculatePoint()算出三个圆点的坐标,在通过传进去的数值来要画多少个圆圈,而原点是不管数值多少都要画的。效果图如下:


img6.PNG

接着就是画解释的内容drawExpain(Canvas canvas)代码如下:

 //画解释的内容 
public void drawExpain(Canvas canvas){ 
    if(!expainWatchDag){
        return ;
    } 
//间隔 int margin=circleRadius/5; 
//画平均步数和对应的数值 
   Rect txtRect=new Rect(); 
   mCenterCircle.setColor(Color.BLACK); 
   mCenterCircle.setTextSize(circleRadius/6); 
   mCenterCircle.setTypeface(Typeface.SANS_SERIF); 
   canvas.drawText(averageCount,0,circleRadius+margin,mCenterCircle); 
   mCenterCircle.setColor(friendColor); mCenterCircle.setTextSize(circleRadius/10); 
  mCenterCircle.getTextBounds(averageCountTxt,0,averageCountTxt.length(),txtRect); 
  canvas.drawText(averageCountTxt,0,circleRadius+margin+(txtRect.bottom-   txtRect.top),mCenterCircle);
 
//画好友排名和对应的数值
  mCenterCircle.setColor(Color.BLACK); 
  mCenterCircle.setTextSize(circleRadius/6); 
  canvas.drawText(firendDrank,-circleRadius,-(circleRadius-  marginCircleSize),mCenterCircle); 
  mCenterCircle.setColor(friendColor); 
  mCenterCircle.setTextSize(circleRadius/10); 
  mCenterCircle.getTextBounds(friendDrankTxt,0,friendDrankTxt.length(),txtRect); 
  canvas.drawText(friendDrankTxt,-circleRadius,-(circleRadius-marginCircleSize)+(txtRect.bottom-txtRect.top),mCenterCircle);
  //画达标天数和对应的数值 
  mCenterCircle.setColor(Color.BLACK); 
  mCenterCircle.setTextSize(circleRadius/6); 
  canvas.drawText(standarDay,circleRadius,-(circleRadius-  marginCircleSize),mCenterCircle); 
  mCenterCircle.setColor(friendColor); 
  mCenterCircle.setTextSize(circleRadius/10); 
  mCenterCircle.getTextBounds(friendDrankTxt,0,friendDrankTxt.length(),txtRect); 
  canvas.drawText(standarDayTxt,circleRadius,-(circleRadius-marginCircleSize)+(txtRect.bottom-txtRect.top),mCenterCircle); centerWatchDag=true; 
}

看起来代码有点多,其实是最简单的,就是确定好友排名的坐标(-circleRadius,-(circleRadius-marginCircleSize)),int margin=circleRadius/5,平均步数的坐标(0,circleRadius+margin),达标天数的坐标(circleRadius,-(circleRadius-marginCircleSize))来进行drawText的操作而已,没什么可以说的,Rect是得出字体大小的,具体看上面代码。效果如下图:


img7.PNG

最后就是画中心圆的内容的centerCircleContent(canvas)了,代码如下:

//画中心圆的内容 
public void centerCircleContent(Canvas canvas){ 
if(!centerWatchDag){ 
return ; 
} 
//画出颜色渐变的圆圈 
  canvas.rotate(140); 
  float centerSize=circleRadius-marginCircleSize*3-(circleRadius/20); 
  mCenterCircle.setShader(new SweepGradient(0,0,new int[]{   friendColor,friendColor,standardColor,averageColor},null)); 
  canvas.drawCircle(0,0,centerSize,mCenterCircle); 
  canvas.rotate(-140); 
//画出运动的总步数 
  mCenterCircle.setShader(null); 
  mCenterCircle.setColor(friendColor); 
  mCenterCircle.setTextSize(circleRadius/4);     
  mCenterCircle.setTextAlign(Paint.Align.CENTER); 
  Rect numRect=new Rect();   
  mCenterCircle.getTextBounds(allStep,0,allStep.length(),numRect); 
  Camera camera=new Camera(); camera.rotateY(centerData); 
  camera.applyToCanvas(canvas); 
  canvas.drawText(allStep,0,(numRect.bottom-numRect.top)/2,mCenterCircle); 
//画出总运动步数右边的字
  Rect tipRect=new Rect(); 
 mCenterCircle.setTextSize(circleRadius/12); 
 mCenterCircle.getTextBounds(tip,0,tip.length(),tipRect); 
 canvas.drawText(tip,(numRect.right-numRect.left)/2+(tipRect.right-tipRect.left)/2+5 ,(numRect.bottom-numRect.top)/2-3,mCenterCircle); 
//画出总运动步数下面的提示 
 Rect theyRect=new Rect(); 
 mCenterCircle.getTextBounds(theyCount,0,theyCount.length(),theyRect); 
 float marginBottom=circleRadius/12; 
 mCenterCircle.setTextSize(circleRadius/11); 
 canvas.drawText(theyCount,0,marginBottom+(numRect.bottom-numRect.top)/2 +(theyRect.bottom-theyRect.top)/2,mCenterCircle);
 }

中心圆的内容里实现的大概思路画解释的内容的思路都差不多,我觉得值得讲的就是这个Camera类了,这里的Camera类可不是相机里的Camera类,他可以实现Camera的旋转缩放的功能,是一个十分强大的类,而camera.rotateY(centerData)就是设置Y轴旋转的效果的关键代码。其次就是用mCenterCircle.setShader(new SweepGradient(0,0,new int[]{ friendColor,friendColor,standardColor,averageColor},null));来实现圆圈颜色的渐变功能的关键代码,里面还可以实现更多效果,这就需要小伙伴们用外的时间学了。最后效果如下:

img8.PNG

至此整个绘画就结束了,接着就是动画效果,代码如下:

 //启动动画的方法 
public void loadAnimator(){
 final ValueAnimator alphaAmimator3=ValueAnimator.ofInt(0,225);
 final ValueAnimator alphaAmimator2=ValueAnimator.ofInt(0,225); 
 final ValueAnimator wavesAminator=ValueAnimator.ofInt(-30,0); 
 final ValueAnimator centerAnimator=ValueAnimator.ofFloat(0,360); 
 ValueAnimator alphaAmimator1=ValueAnimator.ofInt(0,225); 
 centerAnimator.setDuration(1000);
 centerAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {   
  @Override 
  public void onAnimationUpdate(ValueAnimator animation) { 
     centerData=(float)animation.getAnimatedValue(); postInvalidate();
   } 
  });
  wavesAminator.setDuration(1000); 
  wavesAminator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {    
  @Override 
  public void onAnimationUpdate(ValueAnimator animation) { 
   waveData=(int)animation.getAnimatedValue(); 
   waveWatchDag=true; 
   if(waveData==0&&lineWatchDag==false){ 
      lineWatchDag=true; centerAnimator.start(); 
   } 
   postInvalidate(); 
 } }); 
alphaAmimator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override 
public void onAnimationUpdate(ValueAnimator animation) { 
   mCircleAlpha3=(int)animation.getAnimatedValue(); 
   postInvalidate(); 
   if(mCircleAlpha3==225){ 
      wavesAminator.start(); 
  }
 }
 });
 alphaAmimator3.setDuration(250); 
 alphaAmimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override 
public void onAnimationUpdate(ValueAnimator animation) { 
   mCircleAlpha2=(int)animation.getAnimatedValue(); 
   postInvalidate();
   if(mCircleAlpha2==225){
       alphaAmimator3.start(); 
   } 
 } }); 
alphaAmimator2.setDuration(250); 
alphaAmimator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override 
public void onAnimationUpdate(ValueAnimator animation) { 
    mCircleAlpha1=(int)animation.getAnimatedValue(); postInvalidate();    
   if(mCircleAlpha1==225){ 
       alphaAmimator2.start(); 
   } 
}
}); 
alphaAmimator1.setDuration(250); 
alphaAmimator1.start();
}

其实就是通过ValueAnimator不断的生成状态量然后调用postInvalidate()不断的刷新View即可实现。最后要想更详细的了解整个流程请看源码吧。
奉上源码。如果对你有帮助就请给我给星星或喜欢吧

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,835评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,900评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,481评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,303评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,375评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,729评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,877评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,633评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,088评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,443评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,563评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,251评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,827评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,712评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,943评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,240评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,435评论 2 348

推荐阅读更多精彩内容