摘要:今天這篇文章要介紹的是一個(gè)酷炫的進(jìn)度條的設(shè)計(jì)和實(shí)現(xiàn),在進(jìn)度的文字內(nèi)容顏色以及切換的圖片等都可以自由設(shè)置。那么下面我們就開始從無到有實(shí)現(xiàn)一下這個(gè)酷炫的進(jìn)度效果吧。三利用與來實(shí)現(xiàn)進(jìn)度效果。四利用阻尼動(dòng)畫實(shí)現(xiàn)進(jìn)度條回彈效果。
今天這篇文章要介紹的是一個(gè)酷炫的進(jìn)度條的設(shè)計(jì)和實(shí)現(xiàn),在進(jìn)度的文字內(nèi)容、顏色以及切換的圖片等都可以自由設(shè)置。我們先看下效果 (創(chuàng)意受Dribbble的啟發(fā)):
整體效果還是不錯(cuò)的吧,哈哈,我自己還是比較滿意的~項(xiàng)目地址已上傳至 github ,歡迎star、fork。那么下面我們就開始從無到有實(shí)現(xiàn)一下這個(gè)酷炫的進(jìn)度效果吧。
項(xiàng)目地址:SpecialProgressBar。
仔細(xì)觀察下這個(gè)效果,它有不同的動(dòng)態(tài)效果和不同的進(jìn)度狀態(tài)組成,那么實(shí)現(xiàn)的思路就用效果切換的不同狀態(tài)來進(jìn)行切換繪制,對(duì)應(yīng)數(shù)值的變化用到值動(dòng)畫、Path、貝塞爾曲線、Camera與Matrix等相關(guān)工具,因?yàn)樯婕暗牡胤奖容^多,這里我就主要說下大體的實(shí)現(xiàn)思路以及相關(guān)注意點(diǎn)。
主要分五點(diǎn)來進(jìn)行分析:
一、利用值動(dòng)畫變換數(shù)值然后invalidate刷新界面,形成動(dòng)畫效果一、利用值動(dòng)畫變換數(shù)值然后invalidate刷新界面,形成動(dòng)畫效果。
二、在不同的臨界值切換不同的狀態(tài)。
三、利用PathMeasure與Path來實(shí)現(xiàn)進(jìn)度效果。
四、利用阻尼動(dòng)畫實(shí)現(xiàn)進(jìn)度條回彈效果。
五、利用Camera和Matrix實(shí)現(xiàn)進(jìn)度框翻轉(zhuǎn)效果。
對(duì)位置、大小、顏色的切換主要使用的ValueAnimator來進(jìn)行變化,看下代碼片段:
ValueAnimator va = ValueAnimator.ofInt((int)(Math.min(getWidth(), getHeight())- mBgPaint.getStrokeWidth()*2)/2,(int) mBgPaint.getStrokeWidth()); va.setInterpolator(new AnticipateInterpolator()); va.setDuration(800); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int value = (Integer) animation.getAnimatedValue(); radiu = value; center_scaleX = (1 - animation.getAnimatedFraction()); center_scaleY = (1 - animation.getAnimatedFraction()); invalidate(); } }); va.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { state = STATE_READY_CHANGEING;//準(zhǔn)備階段 mBgPaint.setStyle(Paint.Style.STROKE); mBgPaint.setColor(Color.BLACK); changeStateReadyChanging(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); va.start();
通過不同Interpolator插值器實(shí)現(xiàn)不同的運(yùn)動(dòng)效果,在AnimatorUpdateListener中改變數(shù)值,然后調(diào)用invalidat()方法,之后onDraw()方法會(huì)被調(diào)用,我們改變的數(shù)值在界面是就可以看到產(chǎn)生的動(dòng)態(tài)效果了。
二、在不同的臨界值切換不同的狀態(tài)考慮到動(dòng)畫中涉及的動(dòng)畫效果還是比較多的,可以用不同的狀態(tài)表示不同的動(dòng)畫區(qū)間,在動(dòng)畫結(jié)束時(shí)切換不同的狀態(tài),然后在invalidate方法中根據(jù)不同的狀態(tài)進(jìn)行繪制,我們來看下吧:
private static final int STATE_READY = 0; private static final int STATE_READY_CHANGEING = 1; private static final int STATE_READYING = 2; private static final int STATE_ERROR = 3; private static final int STATE_STARTING = 4; private static final int STATE_SUCCESS = 5; private static final int STATE_BACK = 6; private static final int STATE_BACK_HOME = 7; private static final int DONE = 8;
這里定義了九種狀態(tài),代表不同的動(dòng)畫效果區(qū)間,根據(jù)這些狀態(tài)來進(jìn)行動(dòng)態(tài)切換繪制:
switch (state) { case STATE_BACK_HOME: case STATE_READY: p.reset(); mBgPaint.setStyle(Paint.Style.FILL); p.addCircle(getWidth() / 2, getHeight() / 2, radiu, Path.Direction.CCW); canvas.drawPath(p, mBgPaint); matrix.reset(); matrix.setScale(center_scaleX, center_scaleY); matrix.preTranslate(0,0); matrix.postTranslate(getWidth() / 2 - downloadBitmap.getWidth() / 2*Math.max(center_scaleX,center_scaleY), getHeight() / 2 - downloadBitmap.getHeight() / 2*Math.max(center_scaleX,center_scaleY)); canvas.drawBitmap(downloadBitmap, matrix, mBgPaint); break; case STATE_READY_CHANGEING: p.reset(); p.moveTo(startX, startY); p.lineTo(endX, endY); canvas.drawPath(p, mBgPaint); break; ... }
這里的狀態(tài)切換可以說是整個(gè)動(dòng)畫切換的核心,通過對(duì)不同狀態(tài)的切換,然后對(duì)應(yīng)切換不同的值動(dòng)畫,實(shí)現(xiàn)整個(gè)效果的動(dòng)態(tài)銜接。
三、利用PathMeasure與Path來實(shí)現(xiàn)進(jìn)度效果PathMeasure作為一個(gè)輔助工具,在對(duì)Path路徑進(jìn)行處理時(shí)是很方便的,我們來看下它吧:
setPath(Path path, boolean forceClosed)關(guān)聯(lián)一個(gè)Path
isClosed() 是否閉合
getLength() 獲取Path的長度
nextContour() 跳轉(zhuǎn)到下一個(gè)輪廓
getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)截取路徑片段
getPosTan(float distance, float[] pos, float[] tan)獲取指定長度的位置坐標(biāo)及該點(diǎn)切線值
getMatrix(float distance, Matrix matrix, int flags)獲取指定長度的位置坐標(biāo)及該點(diǎn)Matrix
我們這里重點(diǎn)關(guān)注getSegment和getPosTan方法,
getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo),相關(guān)參數(shù):startD 開始截取位置距離 Path 起點(diǎn)的長度,stopD 結(jié)束截取位置距離 Path 起點(diǎn)的長度,dst 截取的 Path 將會(huì)添加到 dst 中,startWithMoveTo 起始點(diǎn)是否使用 moveTo。
在進(jìn)度條變化的時(shí)候我們就使用這個(gè)方法動(dòng)態(tài)的截取從開始位置到當(dāng)前位置的Path值,截取成功dst中就用截取路徑的值,然后調(diào)用drawPath方法繪制出進(jìn)度效果。
需要注意的是:在安卓4.4或者之前的版本,在默認(rèn)開啟硬件加速的情況下,更改 dst 的內(nèi)容后可能繪制會(huì)出現(xiàn)問題,請(qǐng)關(guān)閉硬件加速或者給 dst 添加一個(gè)單個(gè)操作,例如: dst.rLineTo(0, 0)。
getPosTan (float distance, float[] pos, float[] tan)
相關(guān)參數(shù):
distance 距離 Path 起點(diǎn)的長度
pos 該點(diǎn)的坐標(biāo)值
tan 該點(diǎn)的正切值
這里我們用這個(gè)方法來獲取當(dāng)前點(diǎn)的坐標(biāo)值,如果獲取成功,pos中就有坐標(biāo)值了,通過這個(gè)坐標(biāo)值來動(dòng)態(tài)改變進(jìn)度框和進(jìn)度文字的位置。
四、利用阻尼動(dòng)畫實(shí)現(xiàn)進(jìn)度條回彈效果開始進(jìn)度前進(jìn)度條有個(gè)回彈的效果,這里我們使用的阻尼效果,主要用設(shè)置插值器動(dòng)態(tài)改變二階貝塞爾曲線的定點(diǎn),定點(diǎn)位置的改變,形成整個(gè)路徑效果的改變。
阻尼插值器參考網(wǎng)上的實(shí)現(xiàn),我們看下主要實(shí)現(xiàn):
public DampingInterpolator(int count, float overshoot) { setOverShootCount(count); setOverShootPercent(overshoot); } public void setOverShootCount(int count) { mCount = Math.max(1, count); mRegion = (float) (Math.PI * 2 * (mCount - 1) + Math.PI / 2 * 3); mOvershootModulus = (float) Math.pow(mOvershootPercent, mRegion / Math.PI); } public void setOverShootPercent(float overshoot) { mOvershootPercent = Math.max(0, Math.min(1, overshoot)); /* * 當(dāng) t * mRegion = Math.PI 的時(shí)候,達(dá)到第一次過沖的峰值, 則 t = Math.PI / mRegion 。 且此時(shí) * mOvershootModulus^t = mOvershootPercent , 所以 mOvershootModulus = * Math.pow(mOvershootPercent, 1 / t) , 即 mOvershootModulus = * Math.pow(mOvershootPercent, mRegion / Math.PI) 。 */ mOvershootModulus = (float) Math.pow(mOvershootPercent, mRegion / Math.PI); } @Override public float getInterpolation(float t) { if (t <= 0) { return 0; } if (t >= 1) { return 1; } return (float) (1 - Math.pow(mOvershootModulus, t) * Math.cos(mRegion * t)); }
將阻尼插值器設(shè)置給我們要開啟的值動(dòng)畫,改變二階貝塞爾曲線的定點(diǎn),定點(diǎn)的來回回彈,最終形成曲線的來回回彈。
五、利用Camera和Matrix實(shí)現(xiàn)進(jìn)度框翻轉(zhuǎn)效果在失敗和成功時(shí),進(jìn)度框有個(gè)沿X軸和沿Y軸旋轉(zhuǎn)的效果,如果這里單單使用matrix不能實(shí)現(xiàn)效果,僅僅是在平面沿Z軸旋轉(zhuǎn)的。為了實(shí)現(xiàn)整個(gè)效果,我們使用Camera和Matrix來進(jìn)行實(shí)現(xiàn),調(diào)用camera的roateX和roateY方法進(jìn)行旋轉(zhuǎn)。
看下具體代碼:
camera.save(); camera.rotateY(rotateY); camera.getMatrix(cameraMatrix); camera.restore(); cameraMatrix.preTranslate(0, -loadingBitmap.getHeight() / 2); cameraMatrix.postTranslate(POS[0], POS[1] - loadingBitmap.getHeight() / 2); canvas.drawBitmap(loadingBitmap, cameraMatrix, mBgPaint);
我們?cè)谡{(diào)用rotateY方法后獲取到Matrix,然后調(diào)用canvas的drawBitmap方法來動(dòng)態(tài)改變進(jìn)度框的位置和文字的位置,一個(gè)動(dòng)態(tài)效果就出來啦~
這里主要把主要的難點(diǎn)和主題思路縷了一下,如果要關(guān)注具體細(xì)節(jié),可查看源碼。
【github地址:https://github.com/zhangke301...】如果喜歡,歡迎star、fork。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/65052.html
摘要:我們一貫的理念我們我笑哭一貫的理念是信仰和。第一點(diǎn)視差粒子幾行代碼為了看起來更簡潔,定義視差粒子層數(shù)的屬性就省略了,因?yàn)楸旧硭褪菍樱餐玫摹Q菔舅膶樱瑸榱俗尨蠹夷芨私鈱傩缘氖褂梅椒āS指叽笊希挚梢跃徑饧虞d的等待心情。 JParticles 2.0 發(fā)布,打造炫酷的粒子特效。不好意思哈,在這么繁花似錦的世界里,標(biāo)題不得不取得吸引眼球一點(diǎn)哈,不然...還是不啰嗦了,我們進(jìn)入正題吧s...
閱讀 3362·2023-04-26 03:05
閱讀 1466·2019-08-30 13:09
閱讀 1914·2019-08-30 13:05
閱讀 893·2019-08-29 12:42
閱讀 1390·2019-08-28 18:18
閱讀 3451·2019-08-28 18:09
閱讀 521·2019-08-28 18:00
閱讀 1720·2019-08-26 12:10