Android自定義View-圓形進度條

好幾天不寫博客了余寥,這段時間一直沒時間,感覺一直在忙悯森,但是進度不大宋舷。
好了,言歸正傳瓢姻,最近項目里要用到這么一個自定義view祝蝠,是一個圓形的進度圓環(huán),現(xiàn)在學(xué)習(xí)下怎么來自定義它幻碱。


image.png
源碼下載地址

https://github.com/baojie0327/ViewAndGroup

自定義之前先分析一下续膳,這個自定義View主要有以下幾個部分組成:

  • 最外層的圓環(huán)
  • 圓環(huán)上的小圓點,會隨著進度移動
  • 圓弧收班,會隨著小圓點移動,圓環(huán)上有個漸變度
  • 文字
  • 添加動畫

1. 第一步還是先考慮一下進度圓環(huán)的屬性信息谒兄。

我這里可以設(shè)置的有摔桦,背景顏色,最外層圓環(huán)的寬度,最外層圓環(huán)的顏色邻耕,進度圓環(huán)的顏色(只不過這里設(shè)置了一個漸進度)鸥咖,圓環(huán)上小圓點的顏色,大小兄世,還有一些文字的設(shè)置啼辣,距離的設(shè)置等。

<!--CircleProgress,血壓測量進度動畫-->
    <declare-styleable name="CircleProgress">
        <!--背景-->
        <attr name="backGroundColor" format="color"></attr>
        <!--圓環(huán)的顏色-->
        <attr name="ringColor" format="color"></attr>
        <!--圓環(huán)的寬度-->
        <attr name="ringSize" format="dimension"></attr>
        <!--進度圓環(huán)的顏色-->
        <attr name="ringprogressColor" format="color"></attr>

        <!--圓環(huán)上的小圓點顏色-->
        <attr name="dotColor" format="color"></attr>
        <!--圓環(huán)上的小圓點大小-->
        <attr name="dotSize" format="dimension"></attr>

        <!--進度字體顏色-->
        <attr name="textProgressColor" format="color"></attr>
        <!--進度字體大小-->
        <attr name="textProgressSize" format="dimension"></attr>

        <!--百分號顏色-->
        <attr name="textPercentColor" format="color"></attr>

        <!--固定字體設(shè)置-->
        <attr name="showProgressText" format="string"></attr>
        <!--固定字體顏色-->
        <attr name="texColor" format="color"></attr>
        <!--固定字體大小-->
        <attr name="texSize" format="dimension"></attr>

        <!--文字之間的距離-->
        <attr name="texMarginSize" format="dimension"></attr>

        <!--固定字體大小-->
        <attr name="setNumber" format="integer"></attr>

    </declare-styleable>

2. 當(dāng)然御滩,我們自定義view的名字必須叫CircleProgress鸥拧,和上面attrs.xml里的名字相同,你可以試試如果不同會出現(xiàn)什么后果削解。

獲得我們在attrs.xml中定義的屬性
  public CircleProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //獲得atts.xml定義的屬性值富弦,存儲在TypedArray中
        TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircleProgress, defStyleAttr, 0);
        int n = ta.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = ta.getIndex(i);
            switch (attr) {


                case R.styleable.CircleProgress_ringColors: //圓環(huán)顏色
                    ring_color = ta.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.CircleProgress_ringSize: //圓環(huán)寬度
                    ringSize = ta.getDimension(R.styleable.CircleProgress_ringSize, 13);
                    break;
                case R.styleable.CircleProgress_ringprogressColor: //圓環(huán)進度顏色
                    ring_progress_color = ta.getColor(attr, Color.WHITE);
                    break;

                case R.styleable.CircleProgress_dotColor:  //小圓點
                    dot_color = ta.getColor(attr, Color.WHITE);
                    break;
                case R.styleable.CircleProgress_dotSize:  //小圓點大小
                    dotSize = ta.getDimension(R.styleable.CircleProgress_dotSize, 32);
                    break;

                case R.styleable.CircleProgress_textProgressColor: //字體進度的顏色
                    text_progress_color = ta.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.CircleProgress_textProgressSize:  //字體進度大小
                    textProgressSize = ta.getDimension(R.styleable.CircleProgress_textProgressSize, 32);
                    break;

                case R.styleable.CircleProgress_textPercentColor:  //百分號顏色
                    percent_color = ta.getColor(attr, Color.BLACK);
                    break;

                case R.styleable.CircleProgress_showProgressText:  //固定字體顯示
                    showText = ta.getString(R.styleable.CircleProgress_showProgressText);
                    break;
                case R.styleable.CircleProgress_texColor: //字體的顏色
                    text_color = ta.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.CircleProgress_texSize:  //字體大小
                    texSize = ta.getDimension(R.styleable.CircleProgress_texSize, 17);
                    break;

                case R.styleable.CircleProgress_texMarginSize:  //字之間的款阿杜
                    texMarginSize = ta.getDimension(R.styleable.CircleProgress_texMarginSize, 9);
                    break;
            }
        }
        ta.recycle();
        init();
    }
重寫onMeasure()方法,讓控件支持wrap_content屬性
 /**
     * 重寫onMeasure()方法氛驮,支持wrap_content屬性
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width;
        int height;
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);  //寬度的測量模式
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);  //寬度的測量值
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);  //高度的測量模式
        int heightSize = MeasureSpec.getSize(heightMeasureSpec); //高度的測量值
        //如果布局里面設(shè)置的是固定值,這里取布局里面的固定值;如果設(shè)置的是match_parent,則取父布局的大小
        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            //如果布局里面沒有設(shè)置固定值,這里取布局的寬度的1/2
            width = widthSize * 1 / 2;
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            //如果布局里面沒有設(shè)置固定值,這里取布局的高度的3/4
            height = heightSize * 3 / 4;
        }

        setMeasuredDimension(width, height);

    }
畫最外層的圓
 // 畫最外層的大圓環(huán)
        int centre = getWidth() / 2; //獲取圓心的x坐標
        int radius = (int) (centre - 2 * ringSize); //圓環(huán)的半徑
        mPaint.setColor(ring_color); //設(shè)置圓環(huán)的顏色
        mPaint.setStyle(Paint.Style.STROKE); //設(shè)置空心
        mPaint.setStrokeWidth(ringSize); //設(shè)置圓環(huán)的寬度
        mPaint.setAntiAlias(true);  //消除鋸齒
        canvas.drawCircle(centre, centre, radius, mPaint); //畫出圓環(huán)
畫文字
 //畫進度文字
        //設(shè)置進度值的大小顏色腕柜,字體樣式
        textPaint.setStrokeWidth(0);
        textPaint.setColor(text_progress_color);
        textPaint.setTextSize(textProgressSize);
        textPaint.setTypeface(Typeface.MONOSPACE);

        //中間的進度百分比,先轉(zhuǎn)換成float在進行除法運算矫废,不然都為0
        int percent = (int) (((float) tempProgress / (float) maxProgress) * 100);
        String percent_draw;
        if (percent == 0) {
            percent_draw = "00";
        } else {
            percent_draw = percent + "";
        }
        float textHeight = textProgressSize; //進度字體的高度
        float textWidth = textPaint.measureText(percent_draw);

        textPaint.setTextSize(textHeight);
        //   canvas.drawText(percent_draw, centre - 5 * textWidth, centre, textPaint);
        canvas.drawText(percent_draw, centre - textWidth / 2, centre + textProgressSize / 6, textPaint);

        //畫百分比號
        textPaint.setStrokeWidth(3);
        String text_percent = "%";
        textPaint.setTextSize(textHeight / 3);
        // canvas.drawText(text_percent, centre + 5 * textWidth , centre + textWidth / 2, textPaint);
        canvas.drawText("%", centre + textWidth / 2, centre + textProgressSize / 8, textPaint);

        //畫展示文字
        textPaint.setColor(text_color);
        textPaint.setTextSize(texSize);
        canvas.drawText(showText, centre - textProgressSize / 2 - 10, centre + textProgressSize / 3 + texMarginSize, textPaint);
畫圓弧

在畫圓弧的過程中盏缤,我們創(chuàng)建了一個SweepGradient渲染器,來畫我們的漸進色圓環(huán)蓖扑。直接通過 ringProgressPaint.setShader(mSweepGradient)設(shè)置給Paint即可

        //畫圓弧
        ringProgressPaint.setStyle(Paint.Style.STROKE);
        ringProgressPaint.setStrokeWidth(ringSize);

        //  ringProgressPaint.setColor(ring_progress_color);
        RectF oval = new RectF(centre - radius, centre - radius, centre
                + radius, centre + radius);  //用于定義的圓弧的形狀和大小的界限

        //創(chuàng)建一個渲染器
        SweepGradient mSweepGradient=new SweepGradient(canvas.getWidth()/2
                ,canvas.getHeight()/2,new int[]{Color.rgb(130,213,131),Color.rgb(150,251,196),Color.rgb(130,213,131)},null);
        Matrix matrix=new Matrix();
        matrix.setRotate(-90f,canvas.getWidth()/2,canvas.getHeight()/2);
        mSweepGradient.setLocalMatrix(matrix);
        ringProgressPaint.setShader(mSweepGradient);
        canvas.drawArc(oval, 90, 360 * tempProgress / maxProgress, false, ringProgressPaint);
畫小圓點

其中有個計算來計算小圓點的坐標唉铜,你可以修改這個計算部分來設(shè)置小圓點的位置,我把它放在了最下面的位置赵誓。

//畫圓點
        // 畫進度點   30°角度 的弧度 = 2 * PI / 360 * 30
        int rangle = 0;
        if (tempProgress == 0) {
            rangle = 360 / maxProgress;
        } else {
            rangle = 360 * (int) tempProgress / maxProgress;
        }

        double a = 0.0;//角度
        int pointX = 0;
        int pointY = 0;


        if (rangle > 0 && rangle <= 90) {
            a = 2 * Math.PI / 360 * (270 - rangle);
            pointX = centre + (int) (radius * Math.cos(a));
            pointY = centre - (int) (radius * Math.sin(a));
        } else if (rangle > 90 && rangle <= 180) {
            a = 2 * Math.PI / 360 * (rangle + 90);
            pointX = centre + (int) (radius * Math.cos(a));
            pointY = centre + (int) (radius * Math.sin(a));
        } else if (rangle > 180 && rangle <= 270) {
            a = 2 * Math.PI / 360 * (rangle);
            pointX = centre - (int) (radius * Math.sin(a));
            pointY = centre + (int) (radius * Math.cos(a));
        } else if (rangle > 270 && rangle <= 360) {
            a = 2 * Math.PI / 360 * (rangle - 90);
            pointX = centre - (int) (radius * Math.cos(a));
            pointY = centre - (int) (radius * Math.sin(a));
        }

        pointPaint.setColor(dot_color);
        pointPaint.setStyle(Paint.Style.FILL);
        pointPaint.setAntiAlias(true);  //消除鋸齒
        pointPaint.setShadowLayer(10, 0, 0, Color.GRAY);
        //  Log.d("TAG", "pointX = " + pointX + "||pointY = " + pointY);
        canvas.drawCircle(pointX, pointY, dotSize, pointPaint);
其他的方法

主要是設(shè)置圓環(huán)的進度和動畫的一些方法

 public synchronized void setMax(int maxProgress) {
        if (maxProgress < 0) {
            throw new IllegalArgumentException("maxProgress not less than 0");
        }
        this.maxProgress = maxProgress;
    }

    /**
     * 開啟動畫
     *
     * @param curProgress
     */
    public void setProgressWithAnimation(int curProgress) {
        this.curProgress = curProgress;
        animator = ValueAnimator.ofInt(0, curProgress);
        animator.setDuration(30000);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                tempProgress = (int) animation.getAnimatedValue();
                invalidate();
            }
        });
        animator.start();

    }

    /**
     * 完成測量
     *
     * @param curProgress
     */
    public void completeMeasure(int curProgress) {
        animator.cancel();
        this.curProgress = curProgress;
        tempProgress = curProgress;
        invalidate();
    }

    /**
     * 停止Progress
     */
    public void stopProgress(){
        animator.cancel();
        invalidate();
    }

這里自定義的CircleProgress就定義完了打毛,接下來可以在xml文件中引用,然后通過下面兩行代碼開啟

  mCircleProgress.setMax(100);
  mCircleProgress.setProgressWithAnimation(100);

當(dāng)然你也可以根據(jù)自己的需求添加其他的方法俩功,再次就不一一添加了幻枉。
直接運行看效果圖吧

Animation1.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市诡蜓,隨后出現(xiàn)的幾起案子熬甫,更是在濱河造成了極大的恐慌,老刑警劉巖蔓罚,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件椿肩,死亡現(xiàn)場離奇詭異,居然都是意外死亡豺谈,警方通過查閱死者的電腦和手機郑象,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茬末,“玉大人厂榛,你說我怎么就攤上這事盖矫。” “怎么了击奶?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵辈双,是天一觀的道長。 經(jīng)常有香客問我柜砾,道長湃望,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任痰驱,我火速辦了婚禮证芭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘萄唇。我一直安慰自己檩帐,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布另萤。 她就那樣靜靜地躺著湃密,像睡著了一般。 火紅的嫁衣襯著肌膚如雪四敞。 梳的紋絲不亂的頭發(fā)上泛源,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天,我揣著相機與錄音忿危,去河邊找鬼达箍。 笑死,一個胖子當(dāng)著我的面吹牛铺厨,可吹牛的內(nèi)容都是我干的缎玫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼解滓,長吁一口氣:“原來是場噩夢啊……” “哼赃磨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起洼裤,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤邻辉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后腮鞍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體值骇,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年移国,在試婚紗的時候發(fā)現(xiàn)自己被綠了吱瘩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡迹缀,死狀恐怖使碾,靈堂內(nèi)的尸體忽然破棺而出皱卓,到底是詐尸還是另有隱情,我是刑警寧澤部逮,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站嫂易,受9級特大地震影響兄朋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜怜械,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一颅和、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缕允,春花似錦峡扩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至驾霜,卻和暖如春案训,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背粪糙。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工强霎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蓉冈。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓城舞,卻偏偏與公主長得像,于是被迫代替她去往敵國和親寞酿。 傳聞我的和親對象是個殘疾皇子家夺,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,585評論 2 359

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,302評論 25 707
  • packagecom.andbase.elememe.widget; importandroid.content....
    那個唐僧閱讀 480評論 2 1
  • 版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載熟嫩。微博:厲圣杰微信公眾號:牙鍋子(基本不更)源碼:CircleP...
    牙鍋子閱讀 17,517評論 24 86
  • 序言:最近一段時間由于要忙學(xué)校畢業(yè)的事情和公司項目的事情秦踪,很久沒有更新博客了,這兩天項目中有個需要用到圓形進度條的...
    24K純帥豆閱讀 23,192評論 8 50
  • 藍天掸茅、白云下的湖面椅邓,往往能夠造就一幅震撼人心的美景。2014年途徑米切爾湖(Mitchell Lake)時昧狮,曾經(jīng)見...
    田園讀書人閱讀 955評論 4 10