高仿QQ運動的周報界面

這次高仿的是QQ運動的周報界面的網(wǎng)圖。這個控件剛開始的時候以為代碼量不大,沒想到一路下來界面代碼在加上動畫代碼還是蠻多的。好了老規(guī)矩先上圖:


效果圖.gif

效果還是和qq的才不多吧。 1. 首先我把各個變量都貼出來以便在后續(xù)中你們可以更好理解代碼的意思:

//屏幕的寬度 
private int mScreemWidth; 
//屏幕的高度 
private int mScreemHight;
 //圓的線 
private Paint mCirclePaint; 
//圓區(qū)域的顏色 
private Paint mCirclePaintColor; 
//虛線 
private Paint mLineCircle;
 //圓點 
private Paint mCircleHoldPaint; 
//畫字體 
private Paint mCenterCircle; 
//最外的圓的透明度 
private int mCircleAlpha1=0; 
//中間的圓的透明度 
private int mCircleAlpha2=0; 
//最內(nèi)的圓的透明度 
private int mCircleAlpha3=0; 
//好友排名 
private int mFriendDranking=0; 
//達(dá)標(biāo)天數(shù) 
private int mStandardDay=0; 
//平均步數(shù) 
private int mAverageCount=0; 
//好友排名的X軸坐標(biāo) 
private float mFriendDrankingX=0; 
//好友排名的Y軸坐標(biāo) 
private float mFriendDrankingY=0; 
//平均步數(shù)的X軸坐標(biāo)
private float mStandardDayX=0; 
//平均步數(shù)的Y軸坐標(biāo) 
private float mStandardDayY=0;
 //達(dá)標(biāo)天數(shù)的X軸坐標(biāo) 
private float mAverageCountX=0; 
//達(dá)標(biāo)天數(shù)的Y軸坐標(biāo) 
private float mAverageCountY=0; 
//臨時的View的半徑 
private int tempCircleRadius=0; 
//View的半徑 
private int circleRadius=0; 
//每個圓圈的間隔 
private float marginCircleSize=0; 
//圓的顏色 
private int circleColor=0; 
//朋友區(qū)域的顏色 
private int friendColor; 
//平均步數(shù)區(qū)域的顏色 
private int averageColor; 
//達(dá)標(biāo)天數(shù)區(qū)域的顏色 
private int standardColor; 
//總步數(shù) 
private String allStep; 
//好友排名
private String firendDrank; 
//達(dá)標(biāo)天數(shù) 
private String standarDay; 
//平均步數(shù) 
private String averageCount; 
//波浪動畫的數(shù)值 
private int waveData=-30; 
//中間文字翻轉(zhuǎn)動畫的數(shù)值 
private float centerData=0; 
//畫波浪的看門狗 
private boolean waveWatchDag=false; 
//畫虛線的看門狗 
private boolean lineWatchDag=false; 
//各點解釋的看門狗 
private boolean expainWatchDag=false; 
//中心圓的內(nèi)容的看門狗 
private boolean centerWatchDag=false; 
//解釋的字符串 
private String averageCountTxt="平均步數(shù)"; 
private String friendDrankTxt="好友排名"; 
private String standarDayTxt="達(dá)標(biāo)天數(shù)"; 
private String theyCount="本周總步數(shù)"; 
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.這里當(dāng)設(shè)置大小為wrap_content的時候,View的寬度的話是用屏幕的的寬,而View的高的話是屏幕的高度的一半。當(dāng)View的大小生成之后會調(diào)用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; 
}

當(dāng)View的寬度大于View的高度時,最外邊的圓的半徑就是w/3.4,反之當(dāng)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); 
  //畫解釋的內(nèi)容 
  drawExpain(canvas); 
  //畫中心圓的內(nèi)容 
  centerCircleContent(canvas); 
}

首先把canvas的原點移到X軸為寬度的一半,Y軸為高度的一半再減去六分之一的半徑,保存canvas的狀態(tài)。接著就一個個說這里面的每一個方法:

//畫出三條圓圈 
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); 
//得到達(dá)標(biāo)天數(shù)半徑 
float mStandarDayData=circleValue(mStandardDay); 
//得到平均步數(shù)半徑
float mAverageCountData=circleValue(mAverageCount); 

/*畫好友排名*/ 
//得出左上角的圓的坐標(biāo)
float[] mFriendDrankingPoint=calculatePoint(mFriendDrankingData); 
//好友排名的X軸坐標(biāo) 
mFriendDrankingX=-mFriendDrankingPoint[0]; 
//好友排名的Y軸坐標(biāo) 
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);

 /*畫達(dá)標(biāo)天數(shù)*/ 
//得出右上角的圓的坐標(biāo)
 float[] mStandarDayPoint=calculatePoint(mStandarDayData);
 //達(dá)標(biāo)天數(shù)的X軸坐標(biāo)
 mStandardDayX=mStandarDayPoint[0]; 
//達(dá)標(biāo)天數(shù)的Y軸坐標(biāo) 
mStandardDayY=-mStandarDayPoint[1];
 //畫出還有達(dá)標(biāo)天數(shù)的波浪線 
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); 

/*平均步數(shù)*/
//平均步數(shù)的X軸坐標(biāo) 
mAverageCountX=0; 
//平均步數(shù)的Y軸坐標(biāo) 
mAverageCountY=mAverageCountData;
//畫出還有平均步數(shù)的波浪線 
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); 
}

這方法里最核心的就是數(shù)學(xué)計算了,整個View有3個波浪區(qū)域,各占一個圓的三分之一,所以第一步就是計算出這個圓的左下角,右小角和正上角的三個點,如圖


img2.PNG

的藍(lán)色點所示。具體代碼見注釋。在通過circleValue算出波浪線的半徑:

 //算出弧線區(qū)域的半徑 
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()方法來各個波浪區(qū)域?qū)?yīng)的頂點,代碼如下:

//算出右上角或左上角的坐標(biāo) 
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; 
}

最后轉(zhuǎn)化為形象的圖就是:


img3.PNG

接著用Path把各個區(qū)域的點連起來就是形成區(qū)域,不過現(xiàn)在還是尖角,要把它變成原角就要用mCirclePaintColor.setPathEffect(new CornerPathEffect(20));方法,這樣各個邊的連接處都可以轉(zhuǎn)換成圓角,可是因為是圓角所以到不到圓圈的邊,這時候你要對你的頂點進(jìn)行微調(diào),所以我再頂點都進(jìn)行了減6或者加6的操作。至于我這個6是怎么得出來的,我用的等比例的數(shù)學(xué)方法來求出來的,到時有優(yōu)化我可以把我的方法用代碼表示出來。至此,重要的就說完了,剩下的只是用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){ 
   //畫達(dá)標(biāo)天數(shù)的虛線 
   mLineCircle.setColor(standardColor); 
   drawDottedLine(canvas,judgeDotte(mStandardDay));
   }else if(i==2){ 
   //畫平均步數(shù)的虛線 
  mLineCircle.setColor(averageColor); 
  drawDottedLine(canvas,judgeDotte(mAverageCount)); 
} } 
  canvas.restore(); 
}  

//判斷虛線 
public List<Float> judgeDotte(int value){ List<Float> temp=new ArrayList<>(); 
     if(value==1){ 
       //當(dāng)為1時,波浪頂點到第三個圓  
      temp.add(circleRadius-marginCircleSize*2); 
      temp.add((float)circleRadius); 
      temp.add(circleRadius-marginCircleSize*3); 
    }else if(value==2){ 
     //當(dāng)為2時,波浪頂點到第二個圓 
     temp.add(circleRadius-marginCircleSize); 
     temp.add((float)circleRadius); 
     temp.add(circleRadius-marginCircleSize*3); 
   }else if(value==3){ 
   //當(dāng)為3時,波浪頂點到第一個圓
    temp.add(circleRadius-marginCircleSize*3);
    temp.add((float)circleRadius); 
  } 
   return temp; 
} 
//畫虛線 
public void drawDottedLine(Canvas canvas,List<Float> data){ 
    if(data.size()==2){
       /*當(dāng)數(shù)值是最大的是時候也就是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{ 
    /*當(dāng)數(shù)值在1和2的時候*/ 
   //畫出數(shù)值外的虛線 
   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); 
  //畫出數(shù)值內(nèi)的虛線 
  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通過每次旋轉(zhuǎn)120度來畫出每一條波浪線,通過judgeDotte()方法得出波浪線三個點對應(yīng)的Y軸的坐標(biāo),假如judgeDotte返回的個數(shù)是兩個的話那就是證明頂點在最外面的圓,假如是3個的話就畫出頂點之外和頂點之內(nèi)的線就可以了,代碼注釋已經(jīng)很詳細(xì)了,效果圖如下:


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);
 //畫達(dá)標(biāo)天數(shù)的圓點 
drawCircleHoldImpl(yuan1[0],-yuan1[1],yuan2[0],-yuan2[1], yuan3[0],-yuan3[1],mStandardDay,canvas,standardColor);
 //畫平均步數(shù)的圓點 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){ 
//當(dāng)數(shù)值為3時畫所有圓圈 canvas.drawCircle(mCirlce1X,mCircle1Y,8,mCircleHoldPaint); 
canvas.drawCircle(mCirlce2X,mCircle2Y,8,mCircleHoldPaint); 
}else if(action==2){
 //當(dāng)數(shù)值為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); 
}

這里同樣注釋也是很詳細(xì)的,整個思路就是通過calculatePoint()算出三個圓點的坐標(biāo),在通過傳進(jìn)去的數(shù)值來要畫多少個圓圈,而原點是不管數(shù)值多少都要畫的。效果圖如下:


img6.PNG

接著就是畫解釋的內(nèi)容drawExpain(Canvas canvas)代碼如下:

 //畫解釋的內(nèi)容 
public void drawExpain(Canvas canvas){ 
    if(!expainWatchDag){
        return ;
    } 
//間隔 int margin=circleRadius/5; 
//畫平均步數(shù)和對應(yīng)的數(shù)值 
   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);
 
//畫好友排名和對應(yīng)的數(shù)值
  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);
  //畫達(dá)標(biāo)天數(shù)和對應(yīng)的數(shù)值 
  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; 
}

看起來代碼有點多,其實是最簡單的,就是確定好友排名的坐標(biāo)(-circleRadius,-(circleRadius-marginCircleSize)),int margin=circleRadius/5,平均步數(shù)的坐標(biāo)(0,circleRadius+margin),達(dá)標(biāo)天數(shù)的坐標(biāo)(circleRadius,-(circleRadius-marginCircleSize))來進(jìn)行drawText的操作而已,沒什么可以說的,Rect是得出字體大小的,具體看上面代碼。效果如下圖:


img7.PNG

最后就是畫中心圓的內(nèi)容的centerCircleContent(canvas)了,代碼如下:

//畫中心圓的內(nèi)容 
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); 
//畫出運動的總步數(shù) 
  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); 
//畫出總運動步數(shù)右邊的字
  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); 
//畫出總運動步數(shù)下面的提示 
 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);
 }

中心圓的內(nèi)容里實現(xiàn)的大概思路畫解釋的內(nèi)容的思路都差不多,我覺得值得講的就是這個Camera類了,這里的Camera類可不是相機(jī)里的Camera類,他可以實現(xiàn)Camera的旋轉(zhuǎn)縮放的功能,是一個十分強(qiáng)大的類,而camera.rotateY(centerData)就是設(shè)置Y軸旋轉(zhuǎn)的效果的關(guān)鍵代碼。其次就是用mCenterCircle.setShader(new SweepGradient(0,0,new int[]{ friendColor,friendColor,standardColor,averageColor},null));來實現(xiàn)圓圈顏色的漸變功能的關(guān)鍵代碼,里面還可以實現(xiàn)更多效果,這就需要小伙伴們用外的時間學(xué)了。最后效果如下:

img8.PNG

至此整個繪畫就結(jié)束了,接著就是動畫效果,代碼如下:

 //啟動動畫的方法 
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不斷的生成狀態(tài)量然后調(diào)用postInvalidate()不斷的刷新View即可實現(xiàn)。最后要想更詳細(xì)的了解整個流程請看源碼吧。
奉上源碼。如果對你有幫助就請給我給星星或喜歡吧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容