啦啦啦,今天給大家帶來最近弄的CircleProgress
相關的效果。這裡的效果圖可能還看不出是UC瀏覽器的那個下拉重新整理的效果,不過首先還是要說說這個進度條,在下一篇中將實現真正的下拉重新整理!
話不多說,直接上圖:
特點:就是一個進度條
1、可以設定多種顏色。
2、可以顯示多種狀態(LOADING、SUCCESS、ERROR,其實遠不止這幾種)
3、可以控制是否顯示箭頭
相關準備工作
知識點:
1.Canvas
裡面相關方法
2.drawArc()
畫圓弧的方法
3.drawPath()
畫路徑的方法
4.屬性動畫使用
結果的鉤鉤或者那個叉叉還有那個箭頭都是使用drawPath()
來完成的。
在onDraw裡面對應有四個相關的方法:
1.drawArc(Canvas canvas)
:畫對應的進度
2.drawTriangle(Canvas c, float startAngle, float sweepAngle)
:畫箭頭
3.drawHook(Canvas canvas)
:畫鉤鉤
4.drawError(Canvas canvas)
:畫叉叉
三個動畫控制:
兩個來控制進度條的 startAngle
和sweepAngle
,一個用來控制畫鉤鉤或者畫叉叉的時候的漸變效果!
相關程式碼
三支畫筆
1 2 3 4 5 6 7 8 |
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); |
三個動畫
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
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相關方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
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); } |
上面的程式碼就是相關核心的方法了,其實對應的進度條效果就是控制 startAngle
和sweepAngle
這兩個對應的欄位,然後不斷的呼叫drawArc()
方法。
對於畫鉤鉤或者畫叉叉,就是一個ValueAnimator
,通過百分比控制縮放和畫筆的透明度。
對於drawTriangle()
方法,如果你覺得很眼熟的話也很正常,其實這個就是在SwipeRefreshLayout
裡面抄過來的。。。。。
一開始,我很糾結這個箭頭怎麼才能跟著進度條一起旋轉,自己寫的也是有各種問題,另外mArrowScale
這個引數在裡面其實沒有使用的。
如果沒有offset偏移量,那麼那個path肯定是畫在左上角的。
1 2 |
x=mRingCenterRadius +fBounds.centerX(); y=fBounds.centerY(); |
通過這個一設定,這個path其實就到了右邊的中間靠著圓弧的內側一點去了(因為這裡的半徑減去了圓弧自己的寬度。。),這麼一來,再根據相關的角度旋轉角度,就有一種跟著進度條一直轉的效果了!
對於drawArc()
方法,主要是控制startAngle
和sweepAngle
這兩個變數,mCurrentGlobalAngle
的變化範圍是(0~360),而mCurrentSweepAngle
的變化範圍是(0~360f – MIN_SWEEP_ANGLE * 2),為什麼要減去兩個最小值呢?因為sweepAngle
總會加一個或者總會減去一個最小值,所以最小間距還是MIN_SWEEP_ANGLE
。
至於什麼時候加什麼時候減呢?這裡有一個變數值mModeAppearing
提供記錄!那就是當mObjectAnimatorSweep
的動畫重複的時候,就需要切換一下了。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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下拉重新整理效果(二)
介紹剩餘的下拉重新整理部分,還有就是兩個圓圈的過度效果。。
相關的程式碼請移步 我的github。。。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式