1. 程式人生 > >Android自定義控制元件實現圓形進度CircleProgressBar

Android自定義控制元件實現圓形進度CircleProgressBar

近日有朋友問我有沒有如下圖效果的開源控制元件

這裡寫圖片描述

相信大家無論是用IOS還是Android,都對這種效果不陌生,很多主流APP都會有這樣或類似的效果,之前也打算研究一下這類控制元件的程式碼,苦於一直不知道應該怎麼搜尋這種效果(就是關鍵詞)或者所搜的結果不是自己想要的,所以就一直擱置了下來。
正好朋友需要這種效果,所以就忙裡偷閒寫了一個類似的、更加常見和適用範圍更多的控制元件,效果如下圖所示:

這裡寫圖片描述

自定義上圖所示效果的控制元件時,其實就是用Canvas繪製不同效果,比如漸變圓弧背景、圓周白色分割線、中間文字等,這篇部落格也根據繪製的順序依次闡述。

1.自定義CircleProgressBar,繼承View,並實現響應的建構函式


程式碼如下:

/**
 * Created by WangChunLei on 2016.1.16
 * E-mail:[email protected]
 */
public class GradientProgressBar extends View {
     public GradientProgressBar(Context context) {
        super(context);
        init();
    }

    public GradientProgressBar(Context context, AttributeSet attrs) {
        super
(context, attrs); init(); } public GradientProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } }

其中init方法是對相關畫筆進行初始化的方法,init方法程式碼如下:

    private void init() {
        backCirclePaint = new Paint();
backCirclePaint.setStyle(Paint.Style.STROKE); backCirclePaint.setAntiAlias(true); backCirclePaint.setColor(Color.LTGRAY); backCirclePaint.setStrokeWidth(circleBorderWidth); // backCirclePaint.setMaskFilter(new BlurMaskFilter(20, BlurMaskFilter.Blur.OUTER)); gradientCirclePaint = new Paint(); gradientCirclePaint.setStyle(Paint.Style.STROKE); gradientCirclePaint.setAntiAlias(true); gradientCirclePaint.setColor(Color.LTGRAY); gradientCirclePaint.setStrokeWidth(circleBorderWidth); linePaint = new Paint(); linePaint.setColor(Color.WHITE); linePaint.setStrokeWidth(5); textPaint = new Paint(); textPaint.setAntiAlias(true); textPaint.setTextSize(textSize); textPaint.setColor(Color.BLACK); }

2.測量控制元件的寬高-onMeasure
onMeasure是自定義控制元件的第一步,目的就是測量得到該控制元件應該佔有的寬高尺寸。其中onMeasure方法的程式碼如下:

   @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
        int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(Math.min(measureWidth, measureHeight), Math.min(measureWidth, measureHeight));
    }

貼上onMeasure的程式碼後,大家估計是很少見過測量過程這麼簡單的onMeasure,不要介意,有興趣的同僚們可以細化一下這個測量過程,對不同的測量模式分別進行處理和測量,讓控制元件適配效果更好更完善!
onMeasure方法中,分別獲取期望的寬度和高度,並取其中較小的尺寸作為該控制元件的寬和高。
3.依次繪製不同的控制元件組成部分。
因為控制元件是直接繼承自View,所以不需要再處理onLayout方法,這也是自定義View的難度遠小於自定義ViewGroup的原因,但繼承ViewGroup也並不一定要重寫onMeasure。
要實現如圖所示的效果,需要分以下步驟依次實現
(1)繪製灰色空心圓環
(2)繪製顏色漸變的圓環
(3)繪製圓環上分割的白色線條
(4)繪製百分比文字等。
繪製過程過,後繪製的內容如果與之前繪製的內容存在交集,則後繪製的內容會覆蓋掉之前繪製的內容。
按照上述步驟依次介紹
在繪製過程中,會產生以下成員變數,下文中會用到:

/*圓弧線寬*/
    private float circleBorderWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, getResources().getDisplayMetrics());
    /*內邊距*/
    private float circlePadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, getResources().getDisplayMetrics());
    /*字型大小*/
    private float textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 50, getResources().getDisplayMetrics());
    /*繪製圓周的畫筆*/
    private Paint backCirclePaint;
    /*繪製圓周白色分割線的畫筆*/
    private Paint linePaint;
    /*繪製文字的畫筆*/
    private Paint textPaint;
    /*百分比*/
    private int percent = 0;
    /*漸變圓周顏色陣列*/
    private int[] gradientColorArray = new int[]{Color.GREEN, Color.parseColor("#fe751a"), Color.parseColor("#13be23"), Color.GREEN};
    private Paint gradientCirclePaint;

3.1繪製灰色空心圓環
程式碼如下:

//1.繪製灰色背景圓環
        canvas.drawArc(
                new RectF(circlePadding * 2, circlePadding * 2,
                        getMeasuredWidth() - circlePadding * 2, getMeasuredHeight() - circlePadding * 2), -90, 360, false, backCirclePaint);

其中,-90為繪製圓弧的起始角度,360是圓弧繪製的角度,即sweepAngle.

3.2繪製顏色漸變的圓環

//2.繪製顏色漸變圓環
        LinearGradient linearGradient = new LinearGradient(circlePadding, circlePadding,
                getMeasuredWidth() - circlePadding,
                getMeasuredHeight() - circlePadding,
                gradientColorArray, null, Shader.TileMode.MIRROR);
        gradientCirclePaint.setShader(linearGradient);
        gradientCirclePaint.setShadowLayer(10, 10, 10, Color.RED);
        canvas.drawArc(
                new RectF(circlePadding * 2, circlePadding * 2,
                        getMeasuredWidth() - circlePadding * 2, getMeasuredHeight() - circlePadding * 2), -90, (float) (percent / 100.0) * 360, false, gradientCirclePaint);

其中,linearGradient是Paint的shadow,是為了圓弧的顏色漸變效果的而需要設定的,日常開發中應用頻率不高,但的確是可以實現非常理想的顏色漸變效果。
3.3繪製圓環上分割的白色線條
繪製圓弧上的白色線條時,需要進行一些簡單的運算,比如線條的起始座標startX,startY和線條的終止座標stopX,stopY等,利用簡單的三角函式還是很容易去計算出來的。
效果中,將圓弧使用白色線條平分成100分,每一個的階級為1,可以滿足int型別的百分比與效果圖比例的一致。

        //半徑
        float radius = (getMeasuredWidth() - circlePadding * 3) / 2;
        //X軸中點座標
        int centerX = getMeasuredWidth() / 2;

        //3.繪製100份線段,切分空心圓弧
        for (float i = 0; i < 360; i += 3.6) {
            double rad = i * Math.PI / 180;
            float startX = (float) (centerX + (radius - circleBorderWidth) * Math.sin(rad));
            float startY = (float) (centerX + (radius - circleBorderWidth) * Math.cos(rad));

            float stopX = (float) (centerX + radius * Math.sin(rad) + 1);
            float stopY = (float) (centerX + radius * Math.cos(rad) + 1);

            canvas.drawLine(startX, startY, stopX, stopY, linePaint);
        }

3.4繪製百分比文字等
最後繪製百分比文字。
繪製文字時,為了保持文字的中心點和圓弧的原點一致,需要先測量得到要顯示文字的寬度和高度,然後再進行一些簡單的運算,原理不再贅述,相信大家數學一定都比我好。

        //4.繪製文字
        float textWidth = textPaint.measureText(percent + "%");
        int textHeight = (int) (Math.ceil(textPaint.getFontMetrics().descent - textPaint.getFontMetrics().ascent) + 2);
        canvas.drawText(percent + "%", centerX - textWidth / 2, centerX + textHeight / 4, textPaint);

最後,暴漏一個公共的方法供改變顯示的百分比,程式碼如下:

/**
     * 設定百分比
     *
     * @param percent
     */
    public void setPercent(int percent) {
        if (percent < 0) {
            percent = 0;
        } else if (percent > 100) {
            percent = 100;
        }

        this.percent = percent;
        invalidate();
    }

至此,所有繪製過程簡述完畢,130行程式碼就能實現很炫酷的效果有木有?
最後,貼上專案完整程式碼,供懶得看實現過程的同僚們使用,O(∩_∩)O哈哈~

package com.example.myview;

import android.content.Context;
import android.graphics.*;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

/**
 * Created by WangChunLei on 2016.1.16
 * e-mail:[email protected]
 */
public class GradientProgressBar extends View {
    /*圓弧線寬*/
    private float circleBorderWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, getResources().getDisplayMetrics());
    /*內邊距*/
    private float circlePadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, getResources().getDisplayMetrics());
    /*字型大小*/
    private float textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 50, getResources().getDisplayMetrics());
    /*繪製圓周的畫筆*/
    private Paint backCirclePaint;
    /*繪製圓周白色分割線的畫筆*/
    private Paint linePaint;
    /*繪製文字的畫筆*/
    private Paint textPaint;
    /*百分比*/
    private int percent = 0;
    /*漸變圓周顏色陣列*/
    private int[] gradientColorArray = new int[]{Color.GREEN, Color.parseColor("#fe751a"), Color.parseColor("#13be23"), Color.GREEN};
    private Paint gradientCirclePaint;

    public GradientProgressBar(Context context) {
        super(context);
        init();
    }

    public GradientProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public GradientProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        backCirclePaint = new Paint();
        backCirclePaint.setStyle(Paint.Style.STROKE);
        backCirclePaint.setAntiAlias(true);
        backCirclePaint.setColor(Color.LTGRAY);
        backCirclePaint.setStrokeWidth(circleBorderWidth);
//        backCirclePaint.setMaskFilter(new BlurMaskFilter(20, BlurMaskFilter.Blur.OUTER));

        gradientCirclePaint = new Paint();
        gradientCirclePaint.setStyle(Paint.Style.STROKE);
        gradientCirclePaint.setAntiAlias(true);
        gradientCirclePaint.setColor(Color.LTGRAY);
        gradientCirclePaint.setStrokeWidth(circleBorderWidth);

        linePaint = new Paint();
        linePaint.setColor(Color.WHITE);
        linePaint.setStrokeWidth(5);

        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(textSize);
        textPaint.setColor(Color.BLACK);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
        int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(Math.min(measureWidth, measureHeight), Math.min(measureWidth, measureHeight));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //1.繪製灰色背景圓環
        canvas.drawArc(
                new RectF(circlePadding * 2, circlePadding * 2,
                        getMeasuredWidth() - circlePadding * 2, getMeasuredHeight() - circlePadding * 2), -90, 360, false, backCirclePaint);
        //2.繪製顏色漸變圓環
        LinearGradient linearGradient = new LinearGradient(circlePadding, circlePadding,
                getMeasuredWidth() - circlePadding,
                getMeasuredHeight() - circlePadding,
                gradientColorArray, null, Shader.TileMode.MIRROR);
        gradientCirclePaint.setShader(linearGradient);
        gradientCirclePaint.setShadowLayer(10, 10, 10, Color.RED);
        canvas.drawArc(
                new RectF(circlePadding * 2, circlePadding * 2,
                        getMeasuredWidth() - circlePadding * 2, getMeasuredHeight() - circlePadding * 2), -90, (float) (percent / 100.0) * 360, false, gradientCirclePaint);

        //半徑
        float radius = (getMeasuredWidth() - circlePadding * 3) / 2;
        //X軸中點座標
        int centerX = getMeasuredWidth() / 2;

        //3.繪製100份線段,切分空心圓弧
        for (float i = 0; i < 360; i += 3.6) {
            double rad = i * Math.PI / 180;
            float startX = (float) (centerX + (radius - circleBorderWidth) * Math.sin(rad));
            float startY = (float) (centerX + (radius - circleBorderWidth) * Math.cos(rad));

            float stopX = (float) (centerX + radius * Math.sin(rad) + 1);
            float stopY = (float) (centerX + radius * Math.cos(rad) + 1);

            canvas.drawLine(startX, startY, stopX, stopY, linePaint);
        }

        //4.繪製文字
        float textWidth = textPaint.measureText(percent + "%");
        int textHeight = (int) (Math.ceil(textPaint.getFontMetrics().descent - textPaint.getFontMetrics().ascent) + 2);
        canvas.drawText(percent + "%", centerX - textWidth / 2, centerX + textHeight / 4, textPaint);
    }

    /**
     * 設定百分比
     *
     * @param percent
     */
    public void setPercent(int percent) {
        if (percent < 0) {
            percent = 0;
        } else if (percent > 100) {
            percent = 100;
        }

        this.percent = percent;
        invalidate();
    }
}

相關推薦

Android定義控制元件實現圓形進度CircleProgressBar

近日有朋友問我有沒有如下圖效果的開源控制元件 , 相信大家無論是用IOS還是Android,都對這種效果不陌生,很多主流APP都會有這樣或類似的效果,之前也打算研究一下這類控制元件的程式碼,苦於一直不知道應該怎麼搜尋這種效果(就是關鍵詞)或者所搜的結

Android定義控制元件NumberCircleProgressBar(圓形進度條)的實現

Mode Rotate 實現方式 1.使用unreachedarea的顏色繪製Circle,作為底層; canvas.drawCircle(centerX, centerY,mCircleRadius, mCirclePaint); 2.使用reacherarea的顏色繪製扇形,作為中間層; canvas.

android定義控制元件圓形進度條(帶動畫)

首先貼上圖片: 額,感覺還行吧,就是進度條的顏色醜了點,不過咱是程式設計師,不是美工,配色這種問題當然不在考慮範圍之內了 下面說重點,如何來寫一個這樣的自定義控制元件。 首先,需要有一個灰色的底圖,來作為未填充時的進度條; 然後,根據傳入的當前進度值,繪製填充時的進度圓

Android學習之路------定義控制元件圓形進度條的簡單實現

簡單介紹 主要是通過自定義一個view類,然後通過操作canvas和paint進行效果的實現 Step 1 新建一個attr.xml,這裡主要是為了自定義我們的控制元件屬性,attr開頭的語句表示控制元件的自定義屬性,在這裡為了實現圓形進度條,定義了一

Android定義控制元件圓形頭像

重寫ImageView public class CircleImageView extends ImageView { private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP; p

Android定義控制元件—仿儀表盤進度控制元件ArcProgressBar

開門見山,效果圖如下: 這種效果經常會遇到,但卻一直不知道這個效果圖應該怎麼描述,所以暫且以“儀表盤進度控制元件”來描述,各位博友如果有更好的描述這種效果的詞彙,請回復博文告訴我,在此先謝謝各位博友了! 其實做出這樣的效果並不困難,只需要瞭解自定義控制元

Android定義控制元件--圓形進度條(中間有圖diao)

智慧家居越來越流行,在智慧家居中我們常要表現一些資料的百分比 圓形度條中間加個圖是一種非常流行的自定義View 1.第一步 你首先需要對類進行繼承View public class CircleProgressImageView extends View 2.第二步 要實

Android定義控制元件進度條的四種實現方式

Progress Wheel為GitHub熱門專案,作者是:Todd-Davies,專案地址: https://github.com/Todd-Davies/ProgressWheel 前三種實現方式程式碼出自: http://stormzhang.com/ope

Android定義控制元件實現帶百分比顯示進度條,可定義顏色

介紹 前天做了一個帶百分比顯示的條形進度條,效果如下: 實現 這個自定義進度條, 看起來簡單, 做起來。。。其實也很簡單: 主要通過繼承View類, 並重寫其onDraw方法實現。 思路分為3步: 1. 畫進圖條背景(圖中灰色部分 2. 根據

Android 定義控制元件 優雅實現元素間的分割線 (支援3.0以下)

1、概述話說,隨著Android SDK版本的升級,很多控制元件增加了新的屬性方便我們的使用,比如LinearLayout中多了:divider、showDividers等,用於為其內部元素新增分隔;但是呢,這樣的屬性在較低版本的SDK中不能被支援,那麼,我們在開發過程中,可

Android定義控制元件實現可滑動的開關(switch)

介紹 昨天晚上寫了一個Android的滑動開關, 即SlideSwitch。效果如下: 實現 實現的思路其實很簡單,監聽控制元件上的touch事件,並不斷重新整理,讓滑塊在手指的位置上繪出,達到滑塊跟著手指滑動的顯示效果。 先看一下程式碼

Android定義控制元件 多樣式進度View

系統進度條賊不好用,大小設定不確定,寬高又不確定,所以自信開發了一套還算是比較完備的進度控制元件。 效果大概就這麼多,背景和進度背景都可以使用顏色,或者圖片,圓角,以及進度條寬高,遊標等等

Android 定義控制元件 輕鬆實現360軟體詳情頁

轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/43649913,本文出自:【張鴻洋的部落格】1、概述最近有不少朋友私聊問應用寶、360軟體助手之類的軟體詳情頁怎麼做,剛好,最近有時間就模仿360軟體助手詳情

Android定義控制元件實現滑動選擇開關

前言:今天我們仿照著Google給我們提供的Switch控制元件來進行一次模仿,自己動手打造一個可以換滑動圖片以及背景的圖片。 -----------------分割線--------------- 先看一下google提供的Switc控制元件: 其實用法很簡單就當普通的

Android 定義控制元件:打造流佈局實現熱門搜尋標籤

具體實現 1,自定義一個類繼承GridView /** * 自定義流佈局 * @author zhouyou */ public class ZFlowLayout extends ViewGroup{ // 儲存所有子View priva

android定義控制元件自動換行效果實現

     第一篇部落格裡面有介紹一篇關於自動換行實現諸多自定義控制元件跟各種效果的博文,但是礙於當初技術能力有限,寫的jar包裡的程式碼亂七八糟,在最近忙完了手頭的工作,不經意間翻看了之前的程式碼,真是慘不忍睹,隨決定重新封裝。重新編寫的android-custom-vg前

Android定義控制元件】選擇輸入框的實現

1.場景 Android開發中經常會用到選擇輸入控制元件,比如性別的輸入 2.示例圖片 3.程式碼組成 1)演示用的主介面佈局不多說,這裡採用EditText + 一個自定義的底部列表對話方塊來實現的 2)佈局檔案 <?xml

Android 定義控制元件-----進度展示view

最近在專案中遇到動態展示進度的需求,於是手動擼了一個,不太完善,希望對有相似需求的人有幫助。以下是部分程式碼:import android.content.Context; import android.content.res.TypedArray; import andro

Android定義控制元件實戰——水流波動效果的實現WaveView

   水流波動的波形都是三角波,曲線是正餘弦曲線,但是Android中沒有提供繪製正餘弦曲線的API,好在Path類有個繪製貝塞爾曲線的方法quadTo,繪製出來的是2階的貝塞爾曲線,要想實現波動效果,只能用它來繪製Path曲線。待會兒再講解2階的貝塞爾曲線是怎麼回事,先

Android定義控制元件之百分比圓環進度

首先我們先來看一下效果 分析 我們來看這個進度條應該分為3個小部分 1.中間的圓 2.外邊的圓環 3.中間的文字 分開畫 這3部分就是需要我們自己畫出來的,因此我們需要3根畫筆 //設定中心園的畫筆