Android學習筆記-自定義仿支付寶ProgressBar動畫
阿新 • • 發佈:2019-02-18
最近開始學習自定義控制元件,看到支付寶支付的ProgressBar動畫感覺不錯,就學著也做一個這樣的ProgressBar。
首先看效果圖
原理:
一個執行緒無限改變進度畫弧形,當外部告知結束,通過判斷結果呈現成功或者失敗動畫,通過path座標緩慢變化就可以實現動畫效果
程式碼如下:
<span style="font-weight: normal;">public class LoadingBar extends View { /** * 進度條畫筆 */ private Paint mPaintProgress; /** * 進度條背景 */ private Paint mPaintLoaded; /** * 進度條所在矩形 */ private RectF mRectF; //中心點座標 private int mCenterX, mCenterY; /** * 載入handler */ private Handler mHandlerLoading; /** * 載入執行緒 */ private Runnable mRunnableLoading; /** * 進度條顏色 */ private int mLoadingBarColor = 0xFF00BCD4; /** * 尾部進度 */ private int mProgressFoot = 0; /** * 頭部進度 */ private int mProgressHead = 0; /** * 進度最大值 */ private int maxProgress = 100; /** * 是否正在載入 */ private boolean isLoading; /** * 頭部速度大於尾部 */ private boolean isChanse; /** * 旋轉睡眠時間 */ private static int PROGRESS_DELAY = 5; /** * 載入完成動畫路徑1 */ private Path mPath1; /** * 載入完成動畫路徑2 */ private Path mPath2; /** * 路徑座標 */ private float pathX, pathY; /** * 第二條路徑座標 */ private float pathX2, pathY2; /** * 控制元件寬高 */ private int minSide; /** * 內邊框 */ private static float padding = 20.0f; private Handler mHandlerFailed; private Runnable mRunnableFailed; private Handler mHandlerSuccess; private Runnable mRunnableSuccess; public LoadingBar(Context context) { super(context); init(context, null); } public LoadingBar(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public LoadingBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) { mHandlerLoading = new Handler(); mRunnableLoading = new Runnable() { @Override public void run() { if (isLoading) { if (mProgressHead > mProgressFoot + 95) { isChanse = true; } if (mProgressHead < mProgressFoot + 5) { isChanse = false; } if (isChanse) { mProgressFoot += 2; mProgressHead += 1; } else { mProgressFoot += 1; mProgressHead += 2; } if (mProgressHead >= maxProgress) { mProgressHead = 0; mProgressFoot = mProgressFoot - maxProgress; } setProgressFoot(mProgressFoot); setProgressHead(mProgressHead); mHandlerLoading.postDelayed(mRunnableLoading, PROGRESS_DELAY); } } }; mHandlerFailed = new Handler(); mRunnableFailed = new Runnable() { @Override public void run() { if (pathX < minSide - mCenterX / 3 - padding) { pathX += 5.0f; pathY += 5.0f; setPaint1LineTo(pathX, pathY); mHandlerFailed.postDelayed(mRunnableFailed, PROGRESS_DELAY); } else if (pathX2 > mCenterX / 3 + padding) { pathX2 -= 5.0f; pathY2 += 5.0f; setPaint2LineTo(pathX2, pathY2); mHandlerFailed.postDelayed(mRunnableFailed, PROGRESS_DELAY); } } }; mHandlerSuccess = new Handler(); mRunnableSuccess = new Runnable() { @Override public void run() { if (pathX < mCenterX - 10) { pathX += 5.5f; pathY += 5.0f; setPaint1LineTo(pathX, pathY); mHandlerSuccess.postDelayed(mRunnableSuccess, PROGRESS_DELAY); } else if (pathX < minSide - mCenterX / 4 - padding - 5) { pathX += 5.0f; pathY -= 5.5f; setPaint1LineTo(pathX, pathY); mHandlerSuccess.postDelayed(mRunnableSuccess, PROGRESS_DELAY); } } }; mPaintProgress = new Paint(); mPaintProgress.setAntiAlias(true); mPaintProgress.setColor(mLoadingBarColor); mPaintProgress.setStyle(Paint.Style.STROKE); mPaintProgress.setStrokeWidth(12.0f); mPaintLoaded = new Paint(); mPaintLoaded.setAntiAlias(true); mPaintLoaded.setColor(Color.WHITE); mPaintLoaded.setStyle(Paint.Style.STROKE); mPaintLoaded.setStrokeWidth(13.0f); mRectF = new RectF(); mPath1 = new Path(); mPath2 = new Path(); loading(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); minSide = width < height ? width : height; this.setMeasuredDimension(width, height); mCenterX = minSide / 2; mCenterY = minSide / 2; mRectF.set(padding, padding, minSide - padding, minSide - padding); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (isLoading) { canvas.drawArc(mRectF, 0, 360, false, mPaintLoaded); canvas.drawArc(mRectF, calculateProgressFoot(), calculateProgressHead(), false, mPaintProgress); } else { canvas.drawArc(mRectF, 0, 360, false, mPaintProgress); } canvas.drawPath(mPath1, mPaintProgress); canvas.drawPath(mPath2, mPaintProgress); } private int calculateProgressHead() { return (360 * (mProgressHead - mProgressFoot)) / maxProgress; } private int calculateProgressFoot() { return (360 * mProgressFoot) / maxProgress; } private void setProgressHead(int progress) { this.mProgressHead = progress; postInvalidate(); } private void setProgressFoot(int progress) { this.mProgressFoot = progress; postInvalidate(); } private void setPaint1LineTo(float x, float y) { this.mPath1.lineTo(x, y); postInvalidate(); } private void setPaint2LineTo(float x, float y) { this.mPath2.lineTo(x, y); postInvalidate(); } public void setProgressColor(int color) { this.mPaintProgress.setColor(color); } public void loading() { isLoading = true; isChanse = false; mHandlerLoading.removeCallbacksAndMessages(null); mHandlerLoading.postDelayed(mRunnableLoading, PROGRESS_DELAY); } /** * @param result */ public void loadingComplete(boolean result) { isLoading = false; if (result) { success(); } else { failed(); } } /** * 失敗動畫 */ private void failed() { pathX = padding + mCenterX / 3; pathY = padding + mCenterX / 3; mPath1.moveTo(pathX, pathY); pathX2 = minSide - mCenterX / 3 - padding; pathY2 = padding + mCenterX / 3; mPath2.moveTo(pathX2, pathY2); mHandlerFailed.removeCallbacksAndMessages(null); mHandlerFailed.postDelayed(mRunnableFailed, PROGRESS_DELAY); } /** * 成功動畫 */ private void success() { pathX = padding + mCenterX / 4; pathY = mCenterY; mPath1.moveTo(pathX, pathY); mHandlerSuccess.removeCallbacksAndMessages(null); mHandlerSuccess.postDelayed(mRunnableSuccess, PROGRESS_DELAY); } }</span>
在此基礎上稍作改動做了一個帶百分比的圓形progressbar,可應用到下載等場景
效果如下
下載地址LoadingBar,兩個控制元件在同一個專案中