啦啦啦,今天給大家?guī)碜罱?code>CircleProgress相關(guān)的效果。這里的效果圖可能還看不出是UC瀏覽器的那個下拉刷新的效果,不過首先還是要說說這個進度條,在下一篇中將實現(xiàn)真正的下拉刷新!
話不多說,直接上圖:


特點:就是一個進度條
1、可以設(shè)置多種顏色。
2、可以顯示多種狀態(tài)(LOADING、SUCCESS、ERROR,其實遠不止這幾種)
3、可以控制是否顯示箭頭
相關(guān)準(zhǔn)備工作
知識點:
1.Canvas里面相關(guān)方法
2.drawArc()畫圓弧的方法
3.drawPath()畫路徑的方法
4.屬性動畫使用
結(jié)果的鉤鉤或者那個叉叉還有那個箭頭都是使用drawPath()來完成的。
在onDraw里面對應(yīng)有四個相關(guān)的方法:
1.drawArc(Canvas canvas):畫對應(yīng)的進度
2.drawTriangle(Canvas c, float startAngle, float sweepAngle):畫箭頭
3.drawHook(Canvas canvas):畫鉤鉤
4.drawError(Canvas canvas):畫叉叉
三個動畫控制:
兩個來控制進度條的 startAngle和sweepAngle,一個用來控制畫鉤鉤或者畫叉叉的時候的漸變效果!
相關(guān)代碼
三支畫筆
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Cap.ROUND);
mPaint.setStrokeWidth(mBorderWidth);
mPaint.setColor(mColors[mCurrentColorIndex]);
mHookPaint = new Paint(mPaint);
mArrowPaint = new Paint(mPaint);
三個動畫
private void setupAnimations() {
mObjectAnimatorAngle = ObjectAnimator.ofFloat(this, mAngleProperty, 360f);
mObjectAnimatorAngle.setInterpolator(ANGLE_INTERPOLATOR);
mObjectAnimatorAngle.setDuration(ANGLE_ANIMATOR_DURATION);
mObjectAnimatorAngle.setRepeatMode(ValueAnimator.RESTART);
mObjectAnimatorAngle.setRepeatCount(ValueAnimator.INFINITE);
mObjectAnimatorSweep = ObjectAnimator.ofFloat(this, mSweepProperty, 360f - MIN_SWEEP_ANGLE * 2);
mObjectAnimatorSweep.setInterpolator(SWEEP_INTERPOLATOR);
mObjectAnimatorSweep.setDuration(SWEEP_ANIMATOR_DURATION);
mObjectAnimatorSweep.setRepeatMode(ValueAnimator.RESTART);
mObjectAnimatorSweep.setRepeatCount(ValueAnimator.INFINITE);
mObjectAnimatorSweep.addListener(new SimpleAnimatorListener() {
@Override
public void onAnimationRepeat(Animator animation) {
toggleAppearingMode();
}
});
fractionAnimator = ValueAnimator.ofInt(0, 255);
fractionAnimator.setInterpolator(ANGLE_INTERPOLATOR);
fractionAnimator.setDuration(100);
fractionAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
fraction = animation.getAnimatedFraction();
mHookPaint.setAlpha((Integer) animation.getAnimatedValue());
invalidate();
}
});
}
四個draw相關(guān)方法
private void drawError(Canvas canvas) {
mError.reset();
mError.moveTo(fBounds.centerX() + fBounds.width() * 0.2f * fraction, fBounds.centerY() - fBounds.height() * 0.2f * fraction);
mError.lineTo(fBounds.centerX() - fBounds.width() * 0.2f * fraction, fBounds.centerY() + fBounds.height() * 0.2f * fraction);
mError.moveTo(fBounds.centerX() - fBounds.width() * 0.2f * fraction, fBounds.centerY() - fBounds.height() * 0.2f * fraction);
mError.lineTo(fBounds.centerX() + fBounds.width() * 0.2f * fraction, fBounds.centerY() + fBounds.height() * 0.2f * fraction);
mHookPaint.setColor(mColors[3]);
canvas.drawPath(mError, mHookPaint);
canvas.drawArc(fBounds, 0, 360, false, mHookPaint);
}
private void drawHook(Canvas canvas) {
mHook.reset();
mHook.moveTo(fBounds.centerX() - fBounds.width() * 0.25f * fraction, fBounds.centerY());
mHook.lineTo(fBounds.centerX() - fBounds.width() * 0.1f * fraction, fBounds.centerY() + fBounds.height() * 0.18f * fraction);
mHook.lineTo(fBounds.centerX() + fBounds.width() * 0.25f * fraction, fBounds.centerY() - fBounds.height() * 0.20f * fraction);
mHookPaint.setColor(mColors[0]);
canvas.drawPath(mHook, mHookPaint);
canvas.drawArc(fBounds, 0, 360, false, mHookPaint);
}
private void drawArc(Canvas canvas) {
float startAngle = mCurrentGlobalAngle - mCurrentGlobalAngleOffset;
float sweepAngle = mCurrentSweepAngle;
if (mModeAppearing) {
mPaint.setColor(gradient(mColors[mCurrentColorIndex], mColors[mNextColorIndex],
mCurrentSweepAngle / (360 - MIN_SWEEP_ANGLE * 2)));
sweepAngle += MIN_SWEEP_ANGLE;
} else {
startAngle = startAngle + sweepAngle;
sweepAngle = 360 - sweepAngle - MIN_SWEEP_ANGLE;
}
canvas.drawArc(fBounds, startAngle, sweepAngle, false, mPaint);
if (showArrow) {
drawTriangle(canvas, startAngle, sweepAngle);
}
}
public void drawTriangle(Canvas c, float startAngle, float sweepAngle) {
if (mArrow == null) {
mArrow = new Path();
mArrow.setFillType(Path.FillType.EVEN_ODD);
} else {
mArrow.reset();
}
float x = (float) (mRingCenterRadius * Math.cos(0) + fBounds.centerX());
float y = (float) (mRingCenterRadius * Math.sin(0) + fBounds.centerY());
mArrow.moveTo(0, 0);
mArrow.lineTo(ARROW_WIDTH * mArrowScale, 0);
mArrow.lineTo((ARROW_WIDTH * mArrowScale / 2), (ARROW_HEIGHT
* mArrowScale));
mArrow.offset(x, y);
mArrow.close();
c.rotate(startAngle + sweepAngle, fBounds.centerX(),
fBounds.centerY());
c.drawPath(mArrow, mPaint);
}
上面的代碼就是相關(guān)核心的方法了,其實對應(yīng)的進度條效果就是控制 startAngle和sweepAngle這兩個對應(yīng)的字段,然后不斷的調(diào)用drawArc()方法。
對于畫鉤鉤或者畫叉叉,就是一個ValueAnimator,通過百分比控制縮放和畫筆的透明度。
對于drawTriangle()方法,如果你覺得很眼熟的話也很正常,其實這個就是在SwipeRefreshLayout里面抄過來的。。。。。
一開始,我很糾結(jié)這個箭頭怎么才能跟著進度條一起旋轉(zhuǎn),自己寫的也是有各種問題,另外mArrowScale這個參數(shù)在里面其實沒有使用的。
如果沒有offset偏移量,那么那個path肯定是畫在左上角的。
x=mRingCenterRadius +fBounds.centerX();
y=fBounds.centerY();
通過這個一設(shè)置,這個path其實就到了右邊的中間靠著圓弧的內(nèi)側(cè)一點去了(因為這里的半徑減去了圓弧自己的寬度。。),這么一來,再根據(jù)相關(guān)的角度旋轉(zhuǎn)角度,就有一種跟著進度條一直轉(zhuǎn)的效果了!
對于drawArc()方法,主要是控制startAngle和sweepAngle這兩個變量,mCurrentGlobalAngle的變化范圍是(0360),而`mCurrentSweepAngle`的變化范圍是(0360f - MIN_SWEEP_ANGLE * 2),為什么要減去兩個最小值呢?因為sweepAngle總會加一個或者總會減去一個最小值,所以最小間距還是MIN_SWEEP_ANGLE。
至于什么時候加什么時候減呢?這里有一個變量值mModeAppearing提供記錄!那就是當(dāng)mObjectAnimatorSweep的動畫重復(fù)的時候,就需要切換一下了。。
mObjectAnimatorSweep.addListener(new SimpleAnimatorListener() {
@Override
public void onAnimationRepeat(Animator animation) {
toggleAppearingMode();
}
});
private void toggleAppearingMode() {
mModeAppearing = !mModeAppearing;
if (mModeAppearing) {
mCurrentColorIndex = ++mCurrentColorIndex % 4;
mNextColorIndex = ++mNextColorIndex % 4;
mCurrentGlobalAngleOffset = (mCurrentGlobalAngleOffset + MIN_SWEEP_ANGLE * 2) % 360;
}
}
最終效果圖

下一篇Android 自定義View UC下拉刷新效果(二)
介紹剩余的下拉刷新部分,還有就是兩個圓圈的過度效果。。
相關(guān)的代碼請移步 我的github。。。