首先咱們來分解一下這個動做,首先是一段progressDialog,能夠看作是在請求數據等待過程,而後成功以後顯示成功的動畫,失敗以後顯示失敗的動畫,那麼這裏涉及到三個狀態,加載中、加載成功和加載失敗,這裏咱們使用枚舉來實現這三種狀態。首先呢,咱們先來實現這個等待的進度條:
一、畫一個圓,確切的來講是畫一段圓弧,而後旋轉畫布,在此過程當中不斷修改圓弧的大小,形成一個這樣動態的假象:git
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(getPaddingLeft(), getPaddingTop()); //將當前畫布的點移到getPaddingLeft,getPaddingTop,後面的操做都以該點做爲參照點
if (mStatus == StatusEnum.Loading) { //正在加載
if (startAngle == minAngle) {
sweepAngle += 6;
}
if (sweepAngle >= 300 || startAngle > minAngle) {
startAngle += 6;
if (sweepAngle > 20) {
sweepAngle -= 6;
}
}
if (startAngle > minAngle + 300) {
startAngle %= 360;
minAngle = startAngle;
sweepAngle = 20;
}
canvas.rotate(curAngle += 4, progressRadius, progressRadius); //旋轉的弧長爲4
canvas.drawArc(new RectF(0, 0, progressRadius * 2, progressRadius * 2), startAngle, sweepAngle, false, mPaint);
invalidate();
}
}複製代碼
這裏
startAngle
表示圓弧的起始角度,sweepAngle
表示圓弧掃過的角度,minAngle
是一個過渡值,是爲了幫助startAngle
改變值而用到的。這裏用到了畫弧度的方法,在上一篇博客中我有細講這個方法,若是你還不知道的話請移步Android自定義view之圓形進度條,這裏還用到了rotate
方法,來看一下它的源碼解釋:github
/**
* Preconcat the current matrix with the specified rotation.
*
* @param degrees The amount to rotate, in degrees
* @param px The x-coord for the pivot point (unchanged by the rotation)
* @param py The y-coord for the pivot point (unchanged by the rotation)
*/
public final void rotate(float degrees, float px, float py) {
translate(px, py);
rotate(degrees);
translate(-px, -py);
}複製代碼
這個方法主要是將畫布進行旋轉,咱們能夠看到,先是將畫布平移到某個點,而後再旋轉某個角度,最後再平移回去,這樣作的目的是爲了讓須要旋轉的
View
進行中心對稱旋轉,因此後面傳的PX,PY
值須要是View
寬高的一半,不信的話你能夠去作個實驗;說了這麼多咱們直接來看一下效果:canvas
二、畫成功狀態的動畫,這部分也能夠分紅兩個小部分,先是畫一個圓,而後再畫中間的鉤:
(1)、畫圓:上一篇博客中講了經過進度來畫弧進而來畫整個圓,今天咱們的主角是Path
,因此咱們使用Path
來實現這樣一個效果,仍是先上代碼,經過代碼來說解:bash
//追蹤Path的座標
private PathMeasure mPathMeasure;
//畫圓的Path
private Path mPathCircle;
//截取PathMeasure中的path
private Path mPathCircleDst;
mPaint.setColor(loadSuccessColor);
mPathCircle.addCircle(getWidth() / 2, getWidth() / 2, progressRadius, Path.Direction.CW);
mPathMeasure.setPath(mPathCircle, false);
mPathMeasure.getSegment(0, circleValue * mPathMeasure.getLength(), mPathCircleDst, true);
canvas.drawPath(mPathCircleDst, mPaint);複製代碼
Path
的經常使用方法有,add
一條路徑(任意形狀,任意線條),這裏咱們在path中添加了一個圓的路徑,具體Path
常見的用法以下表所示:eclipse
Path的常見方法 | 方法含義 |
---|---|
moveTo() | 該方法移動後續操做的起點座標 |
lineTo() | 該方法是鏈接起始點與某一點(傳的參數)造成一條線 |
setLastPath() | 該方法是設置Path最後的座標 |
close() | 該方法是將起點座標與終點座標鏈接起來造成一個閉合的圖形(若是始終點左邊能鏈接的話) |
addRect() | 該方法是繪製一個巨型 |
addRoundRect() | 該方法是繪製一個圓角矩形 |
addOval() | 該方法是繪製一個橢圓 |
arcTo() | 該方法是繪製一段圓弧 |
addArc() | 該方法是繪製一段圓弧 |
而後呢,這裏有一個
PathMeasure
,簡單點說,這玩意就是用來實現Path
座標點的追蹤,你也能夠認爲是Path
座標的計算器,具體PathMeasure
的常見的用法以下表所示:ide
PathMeasure的常見方法 | 方法含義 |
---|---|
setPath() | 該方法將path與PathMeasure綁定起來 |
getLength() | 該方法用於得到path路徑的長度 |
getSegment() | 該方法用於截取整個Path的片斷 |
nextContour() | 該方法用於切換到下一個路徑 |
這裏咱們經過動畫從
0——1
之間的變化,來改變所畫圓的弧度:學習
circleAnimator = ValueAnimator.ofFloat(0, 1);
circleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
circleValue = (float) animation.getAnimatedValue();
invalidate();
}
});複製代碼
(2)、接下來畫完圓以後,咱們要開始畫對鉤了:動畫
if (circleValue == 1) { //表示圓畫完了,能夠鉤了
successPath.moveTo(getWidth() / 1 * 3, getWidth() / 2);
successPath.lineTo(getWidth() / 2, getWidth() / 5 * 3);
successPath.lineTo(getWidth() / 3 * 2, getWidth() / 5 * 2);
mPathMeasure.nextContour();
mPathMeasure.setPath(successPath, false);
mPathMeasure.getSegment(0, successValue * mPathMeasure.getLength(), mPathCircleDst, true);
canvas.drawPath(mPathCircleDst, mPaint);
}複製代碼
這裏的座標我是根據UI給的圖大體算出來的,能夠參考下面這張圖的虛線和實現,對鉤的起始座標在座標軸中大體是
getWidth() / 8 * 3
,你也能夠根據你的需求來畫出這個對鉤,其實就是兩段路徑,分別用path
的lineTo
方法來實現:ui
同理,畫叉叉也是同樣的,只要你算出叉在座標軸中的座標就ok了,這裏我也給出一張參考圖:spa
mPaint.setColor(loadFailureColor);
mPathCircle.addCircle(getWidth() / 2, getWidth() / 2, progressRadius, Path.Direction.CW);
mPathMeasure.setPath(mPathCircle, false);
mPathMeasure.getSegment(0, circleValue * mPathMeasure.getLength(), mPathCircleDst, true);
canvas.drawPath(mPathCircleDst, mPaint);
if (circleValue == 1) { //表示圓畫完了,能夠畫叉叉的右邊部分
failurePathRight.moveTo(getWidth() / 3 * 2, getWidth() / 3);
failurePathRight.lineTo(getWidth() / 3, getWidth() / 3 * 2);
mPathMeasure.nextContour();
mPathMeasure.setPath(failurePathRight, false);
mPathMeasure.getSegment(0, failValueRight * mPathMeasure.getLength(), mPathCircleDst, true);
canvas.drawPath(mPathCircleDst, mPaint);
}
if (failValueRight == 1) { //表示叉叉的右邊部分畫完了,能夠畫叉叉的左邊部分
failurePathLeft.moveTo(getWidth() / 3, getWidth() / 3);
failurePathLeft.lineTo(getWidth() / 3 * 2, getWidth() / 3 * 2);
mPathMeasure.nextContour();
mPathMeasure.setPath(failurePathLeft, false);
mPathMeasure.getSegment(0, failValueLeft * mPathMeasure.getLength(), mPathCircleDst, true);
canvas.drawPath(mPathCircleDst, mPaint);
}複製代碼
到此就完成了自定義的原型進度條了。源碼已上傳至Github,有須要的同窗能夠下載下來看看,歡迎Star,Fork