android 自定義酷炫進度條動畫

源起

  • 公司項目用到一個進度條動畫,須要本身繪製從0加載到100的狀況,並且效果須要很酷炫。最後是採用了lottie動畫+一部分自定義View來實現,拆分了出來。事後,以爲其實本身實現這樣的一個效果也不難,便開始瞭如下的嘗試。先看下效果

分析

  1. 分析下:最外層是一個由小圓點順時針旋轉的圓、第二層是直接一個圓、第三層有兩層:內層是由矩形逆時針旋轉的圓,外層是一個圓環、最後是文字展現。
  2. 實現思路是這樣:分紅多個View來實現,第一層圓點是一個View,繪製完成是使用ObjectAnimator進行旋轉;第二層和第三層的外層和文字是一個View,第三層內層矩形是一個View,一樣是繪製完成使用ObjectAnimator旋轉。(這裏爲什麼分紅三個View咧,由於涉及到旋轉動畫,我想實現旋轉的時候,是對畫布canvas對象進行旋轉,而在一個View裏只有一個畫布canvas對象,若是全部都繪製在一個canvas上,則旋轉的時候就都會旋轉,實現不了我要的效果。若是讀者有更好的解決方案歡迎提出)

開幹

第一部分

  1. 進度條動畫 感謝這篇博客做者給我提供了很好的思路,讓我觸類旁通,若是讀者第一部分看的很模糊的話,能夠結合這篇博客享用
  2. 第一層圓點和第三層內層矩形應該怎麼繪製?這裏實現大同小異放在一塊兒說

  1. 看到此圖不用望而生畏,其實很簡單,咱們最終在畫布上繪製的仍是一個一個的圓,藍色圓是真正要繪製的,空白圓是間隔距離,不須要繪製出來。如今總共是100個藍色圓,100個空心圓,每一個圓佔據的角度是360/200=1.8度;繪製完藍色圓之後,調用canvas.ratate()將畫布旋轉3.6度到下一個藍色圓圓心位置開始繪製圓,最終就有100個藍色圓了。
  2. 怎麼繪製藍色圓?須要肯定藍色圓的圓心位置和半徑。咱們能夠在onMeasure()中拿到View的width和height,除以2獲得就是圓心座標(centerX,centerY),R0就是寬或者高/2能夠獲得的半徑,咱們要求出r:

R1 + r = R0
R1 * sin0.9° = r
由以上兩式能夠得出:r = (R0*sin0.9°)/(1+sin0.9°)——R0已知
小藍圓的圓心爲:(centerX,centerY-R0+r)html

  1. 這樣就能夠繪製出來了,這裏把sin0.9°提升到sin1°,讓藍色圓大一點。
    關鍵代碼以下:(爲了美觀,我將100個藍色小圓改爲了50個,並且每次旋轉3.6*2=7.2度,也就是隔開了三個白色小圓的距離,繪製50次,每次轉過了7.2度,50 * 7.2=360度,每次都繪製一個藍色圓,最後是50個小藍圓)

@Override
    protected void onDraw(Canvas canvas) {
        Log.i("onMeasure","執行了onDraw");
        super.onDraw(canvas);

        mSin_1 = (float) Math.sin(Math.toRadians(1));
        // 大圓半徑
        float outerRadius = (getWidth() < getHeight() ? getWidth() : getHeight()) / 2f;
        //小圓點半徑
        float dotRadius = mSin_1 * outerRadius / (1 + mSin_1);
        float centerX = getWidth() / 2f;
        float centerY = getHeight() / 2f;

        mPaint.setStyle(Paint.Style.FILL);
        int count = 0;
        while (count++ < 50) {
            canvas.drawCircle(centerX, centerY - outerRadius + dotRadius, dotRadius, mPaint);
            canvas.rotate(7.2f, centerX, centerY);
        }
    }
複製代碼

第二部分

  1. 繪製矩形造成的圓,原理也是同樣的,只不過把繪製小藍圓變成繪製矩形。繪製矩形須要肯定左上角和右下角這兩個點的位置就能夠了。而後將畫布旋轉某一角度值繼續繪製便可。這裏繪製了50次,每次旋轉10度,總共是500度>360度,保證大於360度便可,多餘的會重複覆蓋,但若是小於360度,就會致使繪製殘缺。

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mPaint.setShader(null);
        mPaint.setAntiAlias(true); // 抗鋸齒
        mPaint.setDither(true); // 防抖動

        // 半徑,這裏減去40是將半徑縮小40
        float outerRadius = (getWidth() < getHeight() ? getWidth() : getHeight()) / 2f-40;
        float centerX = getWidth() / 2f;
        float centerY = getHeight() / 2f;

        mPaint.setStyle(Paint.Style.FILL);
        int count = 0;
        int des = (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP, 6, getResources().getDisplayMetrics());
        while (count++ < 50) {
            canvas.drawRect(centerX-3,centerY-outerRadius,centerX+3,centerY-outerRadius+des,
                    mPaint);
            canvas.rotate(10.0f, centerX, centerY);
        }
    }
複製代碼

第三部分

  1. 繪製圓環和圓,繪製文字這幾個是放到一個View處理的,由於不用涉及到旋轉,因此能夠放到一塊兒繪製。這部分是參考了另一篇博客,有興趣的讀者也能夠享用 自定義進度條
  2. 畫圓這個比較簡單不說,圓環的話,其實就是畫圓弧,畫圓弧的時候會先肯定一個外接矩形,設置畫筆類型爲描邊circlePaint.setStyle(Paint.Style.STROKE);同時不包含圓心,就能夠了。具體的能夠看下這篇博客 drawArc畫圓弧介紹 ,這裏不便展開。
private void drawCircle(Canvas canvas, int center, int radius)
    {
        //畫一個簡單的圓
        firstPaint.setShader(null); // 清除上一次的shader
        firstPaint.setColor(firstColor); // 設置底部圓環的顏色,這裏使用第一種顏色
        firstPaint.setStyle(Paint.Style.STROKE); // 設置繪製的圓爲空心
        canvas.drawCircle(center, center, radius+40, firstPaint); 
        
        //畫一個圓環
        RectF oval = new RectF(center - radius, center - radius, center + radius, center + radius);
        circlePaint.setShader(null);
        // 繪製顏色漸變圓環
        // shader類是Android在圖形變換中很是重要的一個類。Shader在三維軟件中咱們稱之爲着色器,其做用是來給圖像着色。
        LinearGradient linearGradient = new LinearGradient(circleWidth, circleWidth, getMeasuredWidth()
                - circleWidth, getMeasuredHeight() - circleWidth, colorArray, null, Shader.TileMode.MIRROR);
        circlePaint.setShader(linearGradient);
        //這裏注意設置爲描邊類型
        circlePaint.setStyle(Paint.Style.STROKE);
        circlePaint.setShadowLayer(10, 10, 10, Color.RED);
        circlePaint.setColor(secondColor); // 設置圓弧的顏色
        circlePaint.setStrokeCap(Paint.Cap.ROUND); // 把每段圓弧改爲圓角的
        // 計算每次畫圓弧時掃過的角度,這裏計算要注意分母要轉爲float類型,不然alphaAngle永遠爲0
        alphaAngle = currentValue * 360.0f / maxValue * 1.0f;
        canvas.drawArc(oval, -90, alphaAngle, false, circlePaint);
    }
複製代碼
  1. 能夠看到繪製圓環的時候,用到alphaAngle,這個是掃過的角度,根據currentValue當前的進度值,得出當前掃過的角度。maxValue是100最大進度值。更新進度的時候會調用如下這個方法進行更新,在invalidate()被調用後,View會進行重繪,回調onDraw()方法,得出新的alphaAngle角度值,繪製出對應進度的圓環。
public void setProgress(int progress)
    {
        int percent = progress * maxValue / 100;
        if (percent < 0)
        {
            percent = 0;
        }
        if (percent > 100)
        {
            percent = 100;
        }
        this.currentValue = percent;
        //更新View
        invalidate();
    }
複製代碼
  1. 繪製文字也是一樣的道理,經過setProgress()方法來更新currentValue值。這裏繪製文字有個注意的地方:文字的繪製位置居中。drawText()方法的第二三個參數分別是Text文字x,y座標,這裏爲何設置爲center和baseline這兩個值是有緣由的,牆裂推薦這篇博客瞭解緣由:drawText介紹 這部分的主要代碼以下:
private void drawText(Canvas canvas, int center, int radius)
    {
        float result = (currentValue * 100.0f / maxValue * 1.0f); // 計算進度
        String percent = String.format("%.1f", result) + "%";

        textPaint.setTextAlign(Paint.Align.CENTER); // 設置文字居中,文字的x座標要注意
        textPaint.setColor(textColor); // 設置文字顏色
        textPaint.setTextSize(40); // 設置要繪製的文字大小
        textPaint.setStrokeWidth(0); // 注意此處必定要從新設置寬度爲0,不然繪製的文字會重疊
        Rect bounds = new Rect(); // 文字邊框
        textPaint.getTextBounds(percent, 0, percent.length(), bounds); // 得到繪製文字的邊界矩形
        Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt(); // 獲取繪製Text時的四條線
        int baseline = center + (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom; 
        canvas.drawText(percent, center, baseline, textPaint); // 繪製表示進度的文字
    }
複製代碼

總結

  1. 寫到這其實還沒完,只是對裏面的重點剖析了下,剩下的細節讀者能夠看下源碼,我也封裝成了一個依賴庫,有須要可使用。對應的demo使用放到github上,喜歡的能夠點個star。
  • 在項目的build.gradle中添加:
allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
    }
}
複製代碼
  • 在module的build.gradle中添加:
implementation 'com.github.guoxiaozhou:AnimationDemo:0.5'
複製代碼
  • 具體使用demo地址:github.com/guoxiaozhou… 項目中包含依賴庫源碼,有興趣的能夠看看細節,有問題或者錯誤直接聯繫我
相關文章
相關標籤/搜索