Android 菊花加載View

最近想把項(xiàng)目中用到的東西都寫出來(lái)虫啥,剛好做上傳時(shí)手寫了一個(gè)加載控件蔚约,在這就寫下來(lái)。效果如圖:


截圖.png

看到這個(gè)涂籽,會(huì)的人就不說(shuō)了苹祟,很簡(jiǎn)單,不會(huì)的人就想著網(wǎng)上找评雌,但是網(wǎng)上找的有不如意树枫,而且改起來(lái)還要讀懂別人代碼,其他的就要UI切圖景东,做成幀動(dòng)畫砂轻,但是這樣的東西還是沒(méi)必要做成幀動(dòng)畫,而且從這個(gè)入手還可以回顧下自定義view的步驟斤吐,所以還是動(dòng)手寫一個(gè)唄舔清。

創(chuàng)建類,集成View實(shí)現(xiàn)其構(gòu)造方法曲初。

public CircularLinesProgress(Context context) {
        this(context, null);
    }

    public CircularLinesProgress(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircularLinesProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

View的繪制流程是onMeasure体谒、onDraw、onLayout臼婆,onMeasure是計(jì)算我們子View要多大的設(shè)置抒痒,這里我們的加載控件肯定是一個(gè)正方形,不管布局怎么設(shè)置寬高颁褂,我們都取最小的故响,木桶效應(yīng)將其搞成個(gè)正方形傀广,如代碼:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        mHeigth = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(mWidth >= mHeigth ? mHeigth : mWidth, mWidth >= mHeigth ? mHeigth : mWidth);
    }

然后就是畫,就需要我們實(shí)現(xiàn)onDraw方法彩届,里面自帶一個(gè)畫布伪冰,那我們的畫筆呢?而且畫筆還有樣式要設(shè)置樟蠕,找一個(gè)實(shí)例化畫筆最好的地方贮聂,就莫過(guò)于構(gòu)造方法了。

public CircularLinesProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPaint = new Paint();
        mPaint.setDither(true);
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.GRAY);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mLineWidth);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeJoin(Paint.Join.ROUND);


        mChangePaint = new Paint();
        mChangePaint.setDither(true);
        mChangePaint.setAntiAlias(true);
        mChangePaint.setColor(Color.RED);
        mChangePaint.setStyle(Paint.Style.STROKE);
        mChangePaint.setStrokeWidth(mLineWidth);
        mChangePaint.setStrokeCap(Paint.Cap.ROUND);
        mChangePaint.setStrokeJoin(Paint.Join.ROUND);
    }

然后就是畫寨辩,onDraw里面我們先畫灰色的吓懈,我們先上個(gè)圖分析下:


圖片.png

我們實(shí)際看到的刻度線,是不是都夾雜在大圓與小圓的中間靡狞?只看12點(diǎn)方向的刻度耻警,線的兩端X值都是一樣的是圓形也是控件的寬的一半。然后看Y值甸怕,起點(diǎn)的Y是不是圓心的Y往上減去一定數(shù)值甘穿?終點(diǎn)是不是要減去更多?所以順著這個(gè)思路我們就能控制好畫刻度了梢杭,這個(gè)一定數(shù)值我取的是控件高度一半的百分比温兼。那么第二根第三根怎么辦?唉式曲?是不是只要將畫布轉(zhuǎn)一個(gè)角度在劃線是不是就可以了妨托?至于轉(zhuǎn)多少度缸榛,那就是360度除以我們刻度線的總數(shù)了吝羞。如代碼:

   @Override
   protected void onDraw(Canvas canvas) {
       int x = mWidth / 2;
       int y = mHeigth / 2;
       int r = (int) (mWidth * 0.45);
       canvas.save();
       for (int i = 0; i < mLinesNumber; i++) {
            //繪制下層菊花
            canvas.drawLine(x, y - r, x, (float) (y - r + x * 0.4), mPaint);
            canvas.rotate(360 / mLinesNumber, x, y);
       }
       canvas.restore();
   }

這就是畫灰色刻度。

那么就輪到畫紅色進(jìn)度刻度了内颗,仔細(xì)觀察其實(shí)就是在灰色上覆蓋一個(gè)紅色的钧排,進(jìn)度的話用屬性動(dòng)畫控制,如代碼:

public void start() {
        if(valueAnimator==null){
            valueAnimator = ValueAnimator.ofFloat(0, 1.0f);
            valueAnimator.setDuration(1000);
            valueAnimator.setInterpolator(new LinearInterpolator());
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    float value = (float) valueAnimator.getAnimatedValue();
                    setCurrentProgress(value);
                }
            });
            valueAnimator.start();
        }

    }

設(shè)置當(dāng)前進(jìn)度均澳,并調(diào)用刷新

private float mCurrentProgress = 0f;
public void setCurrentProgress(float mCurrentProgress) {
     this.mCurrentProgress = mCurrentProgress;
     postInvalidate();
}

然后再上面剛畫灰色的地方畫紅色

    @Override
    protected void onDraw(Canvas canvas) {
        int x = mWidth / 2;
        int y = mHeigth / 2;
        int r = (int) (mWidth * 0.45);
        canvas.save();
        for (int i = 0; i < mLinesNumber; i++) {
             //繪制下層菊花
             canvas.drawLine(x, y - r, x, (float) (y - r + x * 0.4), mPaint);
             canvas.rotate(360 / mLinesNumber, x, y);
        }
        //應(yīng)該畫多少
        int currentContent = (int) (mLinesNumber * mCurrentProgress);
        for (int i = currentContent; i > 0; i--) {
             //繪制下層菊花
            canvas.drawLine(x, y - r, x, (float) (y - r + x * 0.4), mChangePaint);
            canvas.rotate(360 / mLinesNumber, x, y);
        }
        canvas.restore();
    }

這樣我們就能完成一次循環(huán)了恨溜,但是UI的建議是循環(huán)并且紅色和灰色交替作為進(jìn)度刻度,這樣看著順眼找前。那么動(dòng)畫就要加個(gè)循環(huán)了

public void start() {
        if(valueAnimator==null){
            valueAnimator = ValueAnimator.ofFloat(0, 1.0f);
            valueAnimator.setDuration(1000);
            valueAnimator.setRepeatCount(-1);
            valueAnimator.setInterpolator(new LinearInterpolator());
            valueAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationRepeat(Animator animation) {
                    mRunContent += 1;
                }
            });
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    float value = (float) valueAnimator.getAnimatedValue();
                    setCurrentProgress(value);
                }
            });
            valueAnimator.start();
        }
    }

mRunContent 變量就是控制紅灰交替進(jìn)度了糟袁,修改下onDraw

@Override
    protected void onDraw(Canvas canvas) {
        mPaint.setStrokeWidth(mLineWidth);
        mChangePaint.setStrokeWidth(mLineWidth);
        int x = mWidth / 2;
        int y = mHeigth / 2;
        int r = (int) (mWidth * 0.45);
        canvas.save();
        if (mRunContent % 2 == 0) {
            for (int i = 0; i < mLinesNumber; i++) {
                //繪制下層菊花
                canvas.drawLine(x, y - r, x, (float) (y - r + x * 0.4), mPaint);
                canvas.rotate(360 / mLinesNumber, x, y);
            }

            //應(yīng)該畫多少
            int currentContent = (int) (mLinesNumber * mCurrentProgress);
            for (int i = currentContent; i > 0; i--) {
                //繪制下層菊花
                canvas.drawLine(x, y - r, x, (float) (y - r + x * 0.4), mChangePaint);
                canvas.rotate(360 / mLinesNumber, x, y);
            }
        } else {
            for (int i = 0; i < mLinesNumber; i++) {
                //繪制下層菊花
                canvas.drawLine(x, y - r, x, (float) (y - r + x * 0.4), mChangePaint);
                canvas.rotate(360 / mLinesNumber, x, y);
            }
            //應(yīng)該畫多少
            int currentContent = (int) (mLinesNumber * mCurrentProgress);
            for (int i = currentContent; i > 0; i--) {
                //繪制下層菊花
                canvas.drawLine(x, y - r, x, (float) (y - r + x * 0.4), mPaint);
                canvas.rotate(360 / mLinesNumber, x, y);
            }
        }
        canvas.restore();
    }

好,到此為止躺盛,效果達(dá)到项戴,但是我們用到了動(dòng)畫,就要記得銷毀動(dòng)畫槽惫,所以我們要做一個(gè)銷毀方法周叮,方便與Activity的onDestory綁定辩撑。

public void cancel(){
        if(valueAnimator!=null){
            valueAnimator.cancel();
            valueAnimator.end();
            valueAnimator.addUpdateListener(null);
            valueAnimator.addListener(null);
        }
    }

完整代碼:

public class CircularLinesProgress extends View {
    private int mLinesNumber = 20;
    private int mLineWidth = 6;
    private int mWidth;
    private int mHeigth;
    private Paint mPaint;
    private float mCurrentProgress = 0f;
    private Paint mChangePaint;
    private int mRunContent = 0;
    private ValueAnimator valueAnimator;

    public void setCurrentProgress(float mCurrentProgress) {
        this.mCurrentProgress = mCurrentProgress;
        postInvalidate();
    }

    public CircularLinesProgress(Context context) {
        this(context, null);
    }

    public CircularLinesProgress(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircularLinesProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPaint = new Paint();
        mPaint.setDither(true);
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.GRAY);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mLineWidth);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeJoin(Paint.Join.ROUND);


        mChangePaint = new Paint();
        mChangePaint.setDither(true);
        mChangePaint.setAntiAlias(true);
        mChangePaint.setColor(Color.RED);
        mChangePaint.setStyle(Paint.Style.STROKE);
        mChangePaint.setStrokeWidth(mLineWidth);
        mChangePaint.setStrokeCap(Paint.Cap.ROUND);
        mChangePaint.setStrokeJoin(Paint.Join.ROUND);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        mHeigth = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(mWidth >= mHeigth ? mHeigth : mWidth, mWidth >= mHeigth ? mHeigth : mWidth);

        mLineWidth = mHeigth / mLinesNumber;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setStrokeWidth(mLineWidth);
        mChangePaint.setStrokeWidth(mLineWidth);
        int x = mWidth / 2;
        int y = mHeigth / 2;
        int r = (int) (mWidth * 0.45);
        canvas.save();
        if (mRunContent % 2 == 0) {
            for (int i = 0; i < mLinesNumber; i++) {
                //繪制下層菊花
                canvas.drawLine(x, y - r, x, (float) (y - r + x * 0.4), mPaint);
                canvas.rotate(360 / mLinesNumber, x, y);
            }

            //應(yīng)該畫多少
            int currentContent = (int) (mLinesNumber * mCurrentProgress);
            for (int i = currentContent; i > 0; i--) {
                //繪制下層菊花
                canvas.drawLine(x, y - r, x, (float) (y - r + x * 0.4), mChangePaint);
                canvas.rotate(360 / mLinesNumber, x, y);
            }
        } else {
            for (int i = 0; i < mLinesNumber; i++) {
                //繪制下層菊花
                canvas.drawLine(x, y - r, x, (float) (y - r + x * 0.4), mChangePaint);
                canvas.rotate(360 / mLinesNumber, x, y);
            }
            //應(yīng)該畫多少
            int currentContent = (int) (mLinesNumber * mCurrentProgress);
            for (int i = currentContent; i > 0; i--) {
                //繪制下層菊花
                canvas.drawLine(x, y - r, x, (float) (y - r + x * 0.4), mPaint);
                canvas.rotate(360 / mLinesNumber, x, y);
            }
        }
        canvas.restore();
    }

    public void start() {
        if(valueAnimator==null){
            valueAnimator = ValueAnimator.ofFloat(0, 1.0f);
            valueAnimator.setDuration(1000);
            valueAnimator.setRepeatCount(-1);
            valueAnimator.setInterpolator(new LinearInterpolator());
            valueAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationRepeat(Animator animation) {
                    mRunContent += 1;
                }
            });
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    float value = (float) valueAnimator.getAnimatedValue();
                    setCurrentProgress(value);
                }
            });
            valueAnimator.start();
        }

    }

    public void cancel(){
        if(valueAnimator!=null){
            valueAnimator.cancel();
            valueAnimator.end();
            valueAnimator.addUpdateListener(null);
            valueAnimator.addListener(null);
        }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市仿耽,隨后出現(xiàn)的幾起案子合冀,更是在濱河造成了極大的恐慌,老刑警劉巖项贺,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件君躺,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡敬扛,警方通過(guò)查閱死者的電腦和手機(jī)晰洒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)啥箭,“玉大人谍珊,你說(shuō)我怎么就攤上這事〖苯模” “怎么了砌滞?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)坏怪。 經(jīng)常有香客問(wèn)我贝润,道長(zhǎng),這世上最難降的妖魔是什么铝宵? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任打掘,我火速辦了婚禮,結(jié)果婚禮上鹏秋,老公的妹妹穿的比我還像新娘尊蚁。我一直安慰自己,他們只是感情好侣夷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布横朋。 她就那樣靜靜地躺著,像睡著了一般百拓。 火紅的嫁衣襯著肌膚如雪琴锭。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,610評(píng)論 1 305
  • 那天衙传,我揣著相機(jī)與錄音决帖,去河邊找鬼。 笑死蓖捶,一個(gè)胖子當(dāng)著我的面吹牛地回,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼落君,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼穿香!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起绎速,我...
    開(kāi)封第一講書(shū)人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤皮获,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后纹冤,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體洒宝,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年萌京,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雁歌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡知残,死狀恐怖靠瞎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情求妹,我是刑警寧澤乏盐,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站制恍,受9級(jí)特大地震影響父能,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜净神,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一何吝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鹃唯,春花似錦爱榕、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)型宝。三九已至八匠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間趴酣,已是汗流浹背梨树。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留岖寞,地道東北人抡四。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親指巡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子淑履,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,156評(píng)論 25 707
  • 虔誠(chéng)地做更好的自己 你要多出去走一走 多去見(jiàn)見(jiàn)不同的朋友 多去認(rèn)識(shí)一些多彩的世界 認(rèn)真看完一本書(shū) 少食多餐 學(xué)會(huì)溫...
  • 在社會(huì)感增強(qiáng)后 我醒了 卻又老了 我的快樂(lè)原則被剝奪 留給我一個(gè)現(xiàn)實(shí)的契約 我脆弱的心 卻在撲通撲通的跳個(gè)不停 現(xiàn)...
    梁運(yùn)生閱讀 139評(píng)論 0 0