1. 程式人生 > >Android學習筆記-自定義仿支付寶ProgressBar動畫

Android學習筆記-自定義仿支付寶ProgressBar動畫

最近開始學習自定義控制元件,看到支付寶支付的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,兩個控制元件在同一個專案中