Android 自定義圓形進度條

Android簡易的圓形進度條

自定義View基礎(chǔ)入門看2個系列文章,非常優(yōu)秀的文章悍引。
安卓自定義View教程目錄
HenCoder Android 開發(fā)進階: 自定義 View 1-2 Paint 詳解


Github
項目碼云地址


  • 最近因為項目需要橡淆,點擊按鈕锡宋,實現(xiàn)轉(zhuǎn)一圈的進度條萎战,感覺需求比較簡單瘫证,就自己做了一個自定義View,很簡易揉阎,分享給大家。


    Android技術(shù)棒
  • 功力有限背捌,只能一步步完成功能毙籽,一步步改代碼,最后完成毡庆。

  • 先看看效果坑赡,圖片有壓縮,丟幀么抗,不是很清晰


    效果.GIF

  • 先初始化畫筆毅否,畫個圓圈
public class CircleProgressView extends View
{
    //控件的寬
    private int width;
    //控件的高
    private int height;
    //區(qū)域
    private RectF rectF;

    //底層畫筆
    private Paint paint_base_progress;
    //進度條的畫筆
    private Paint paint_progress;
    //畫筆寬度
    private int strokeWidth = ConvertUtils.dp2px(5);
    //繪制半徑
    private int radius;

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

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

    /**
     * 由于要使用xml配置控件屬性,所以要3個參數(shù)構(gòu)造方法
     *
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public CircleProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
        paint_base_progress = initPaint();
        paint_progress = initPaint();
    }

    /**
     * 初始化畫筆
     */
    private Paint initPaint()
    {
        Paint paint = new Paint();
        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(strokeWidth);
        //筆畫圓潤,用Paint.Cap.ROUND
        paint.setStrokeCap(Paint.Cap.BUTT);
        //抗鋸齒
        paint.setAntiAlias(true);
        return paint;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = MeasureSpec.getSize(widthMeasureSpec);
        height = MeasureSpec.getSize(heightMeasureSpec);

        if (rectF == null)
        {
            //半徑
            if (radius == 0)
            {
                radius = (width > height ? width : height) / 2;
            }
            rectF = new RectF(width / 2 - radius + strokeWidth / 2
                    , height / 2 - radius + strokeWidth / 2
                    , width / 2 + radius - strokeWidth / 2
                    , height / 2 + radius - strokeWidth / 2);
        }
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        //因為圓開始角度是x軸蝇刀,所以給他旋轉(zhuǎn)270°
        canvas.rotate(270f, width / 2, height / 2);
        canvas.drawArc(rectF, 0f, 360f, false, paint_base_progress);
    }
}
畫筆半徑.jpg
  • 這里藍色就是RectF的大小螟加,黑色就是圓環(huán),畫筆其實是中間經(jīng)過內(nèi)容的吞琐,所以捆探,在確定RectF大小的時候,要算上畫筆的一半站粟。

  • 接下來給這個圓圈加個漸變色吧
//底層掃描漸變色
private Shader shader_base_progress;
//底層條淺的色
private int base_progress_light_color;
//底層條深的色
private int base_progress_height_color;
//底層顏色數(shù)組
private int base_progress_colors[];
//底層顏色分配
private float base_progress_positions[];

  @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        ...

        if (shader_base_progress == null)
        {
            //初始化顏色,如果不設(shè)置顏色參數(shù)黍图,就會使用這個顏色參數(shù)
            base_progress_light_color = Color.WHITE;
            base_progress_height_color = Color.BLACK;

            base_progress_colors = new int[]{base_progress_light_color, base_progress_height_color, base_progress_light_color};
            //這個參數(shù)就是顏色的分配,第一種顏色到0.1的位置卒蘸,第二種到0.9的位置雌隅,第三種到1的位置,不要超過1缸沃。
            base_progress_positions = new float[]{0.1f, 0.95f, 1f};

            shader_base_progress = new SweepGradient(width / 2, height / 2
                    , base_progress_colors, base_progress_positions);
            paint_base_progress.setShader(shader_base_progress);
        } 
    }
圓.jpg
 base_progress_colors = new int[]{ Color.WHITE, Color.BLACK, Color.RED};
 //這個參數(shù)就是顏色的分配恰起,第一種顏色到0.1的位置,第二種到0.9的位置趾牧,第三種到1的位置检盼,不要超過1。
 base_progress_positions = new float[]{0.1f, 0.8f, 1f};
圓.jpg
  • 可以看出參數(shù)變化之后翘单,顏色配色比例不同的效果吨枉,為了過渡的自然蹦渣,所以我一般喜歡最后的0.05段補上和第一段一樣的顏色。
  • 這個是底色貌亭,然后進度條的寫法和這個底色一樣樣柬唯,只是顏色變一變就行啦。

  • 下一步圃庭,想辦法讓這個進度條一點點轉(zhuǎn)動塞茅,不是整個旋轉(zhuǎn)愧沟,底層的圓不動褪那,外層的進度條一點點加隔嫡,需要使用Handler
//順時針
private boolean progress_clockwise = true;
//速度,刷新頻率最快60幀,參數(shù)為每次刷新的間隔书在,單位:毫秒
private long speed = 17;
//轉(zhuǎn)一圈的周期灰伟,單位:毫秒
private long period = 3000;
//循環(huán)
private boolean progress_recycler = false;
//暫停
private boolean progress_pause = false;
//開始
private static final int START = 1111;
//結(jié)束
private static final int COMPLETE = 1000;
private Handler handler = new Handler(new Handler.Callback()
{
    @Override
    public boolean handleMessage(Message msg)
    {
        switch (msg.what)
        {
            case START:
                //順時針轉(zhuǎn)
                if (progress_clockwise)
                {
                    //每圈360°不變,共period辣么長的ms儒旬,360f/period算出每ms轉(zhuǎn)的角度栏账,再用結(jié)果*speek就是每次刷新轉(zhuǎn)的角度
                    progress_angle += 360f / period * speed ;
                }
                //逆時針轉(zhuǎn)
                else
                {
                    progress_angle -= 360f / period * speed ;
                }

                //轉(zhuǎn)滿一圈就停止,并且恢復(fù)底色,絕對值>360
                if (Math.abs(progress_angle) > 360)
                {
                    drawing = false;
                    handler.sendEmptyMessage(COMPLETE);
                } else
                {
                    drawing = true;
                    invalidate();
                    //我試過义矛,1ms发笔,10ms,速度都是一樣凉翻,但是100ms了讨,1000ms,就明顯不一樣
                    //打游戲打得多制轰,我懷疑是刷新頻率最快60幀前计,約16.67ms≈17ms,果然如此
                    handler.sendEmptyMessageDelayed(START, speed );
                }
                break;
            case COMPLETE:
                invalidate();
                break;
        }
        return false;
    }
});

 /**
     * 進度條開始
     */
    public void startProgress()
    {
        progress_angle = 0f;
        //每次都清除
        handler.removeMessages(START);
        handler.sendEmptyMessageDelayed(START, speed);
    }

@Override
    protected void onDraw(Canvas canvas)
    {
        ...
        if (drawing)
        {
            canvas.drawArc(rectF, 0f, base_angle, false, paint_base_progress);
            canvas.drawArc(rectF, 0f, progress_angle, false, paint_progress);
        } else
        {
            canvas.drawArc(rectF, 0f, base_angle, false, paint_base_progress);
        }
    }
  • 只要調(diào)用startProgress 方法垃杖,就會開始轉(zhuǎn)啦男杈。停止也簡單,只要handler.remove掉就停下來了调俘。

  • 個人喜好寫一些能用的工具伶棒。。彩库。肤无。所以要寫成可以用xml配置或者動態(tài)代碼變化的控件,而不是每次用的時候骇钦,改View宛渐。
  • res/values/ 目錄下新建一個attrs.xml文件。


    attrs.jpg
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 底層進度條淺顏色 -->
    <attr name="base_progress_light_color" format="color" />
    <!-- 進度條寬度 -->
    <attr name="progress_width" format="dimension" />
    <!-- 順時針轉(zhuǎn) -->
    <attr name="progress_clockwise" format="boolean" />
    <!-- 周期 -->
    <attr name="progress_period" format="integer" />
    <!-- 循環(huán)播放 -->
    <attr name="progress_recycler" format="boolean"/>

    <declare-styleable name="CircleProgressView">
        <!-- 底層進度條淺顏色 -->
        <attr name="base_progress_light_color" />
        <!-- 進度條寬度 -->
        <attr name="progress_width" />
        <!-- 順時針轉(zhuǎn) -->
        <attr name="progress_clockwise" />
        <!-- 周期 -->
        <attr name="progress_period" />
        <!-- 循環(huán)播放 -->
        <attr name="progress_recycler" />

    </declare-styleable>
</resources>
  • 這里列出幾個,format的類型很多種窥翩,color,就是可以用colors.xml下的資源业岁,dimension就是長寬,dp
    寇蚊,px之類的參數(shù)笔时,所以以此類推,其他類型大家也很好理解的幔荒。

  • 定義好參數(shù)之后糊闽,我們就在三個參數(shù)的構(gòu)造方法那里獲取就可以了梳玫;因為xml不一定有配置一些參數(shù)爹梁,這邊也不會走對應(yīng)的初始化,所以關(guān)鍵參數(shù)最好弄一些默認值提澎。

public CircleProgressView(Context context, AttributeSet attrs, int defStyleAttr)
{
    .....

    //獲取自定義樣式的屬性
    TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircleProgressView, defStyleAttr, 0);
    for (int i = 0; i < typedArray.getIndexCount(); i++)
    {
        int attr = typedArray.getIndex(i);
        switch (attr)
        {
            case R.styleable.CircleProgressView_base_progress_light_color:
                base_progress_light_color = typedArray.getColor(attr, Color.WHITE);
                break;       
            case R.styleable.CircleProgressView_progress_radius:
                //轉(zhuǎn)化為px,TypedValue也可以將DIP(DP)轉(zhuǎn)PX
                radius = typedArray.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP
                        , 0, getResources().getDisplayMetrics()));
                break;
            case R.styleable.CircleProgressView_progress_clockwise:
                progress_clockwise = typedArray.getBoolean(attr, true);
                break;
            case R.styleable.CircleProgressView_progress_period:
                period = typedArray.getInteger(attr, 3000);
                break;
                ......
        }
    }
}
  • 最后姚垃,要一些順時針,逆時針盼忌,開始积糯,暫停等,就按照自己的需求谦纱,加入邏輯就好了看成。下載源碼可以看到更多具體細節(jié)。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末跨嘉,一起剝皮案震驚了整個濱河市川慌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌祠乃,老刑警劉巖梦重,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異亮瓷,居然都是意外死亡琴拧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進店門嘱支,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚓胸,“玉大人,你說我怎么就攤上這事除师∨嫔牛” “怎么了?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵馍盟,是天一觀的道長于置。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么八毯? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任搓侄,我火速辦了婚禮,結(jié)果婚禮上话速,老公的妹妹穿的比我還像新娘讶踪。我一直安慰自己,他們只是感情好泊交,可當(dāng)我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布乳讥。 她就那樣靜靜地躺著,像睡著了一般廓俭。 火紅的嫁衣襯著肌膚如雪云石。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天研乒,我揣著相機與錄音汹忠,去河邊找鬼。 笑死雹熬,一個胖子當(dāng)著我的面吹牛宽菜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播竿报,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼铅乡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了烈菌?” 一聲冷哼從身側(cè)響起阵幸,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎僧界,沒想到半個月后侨嘀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡捂襟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年咬腕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片葬荷。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡涨共,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宠漩,到底是詐尸還是另有隱情举反,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布扒吁,位于F島的核電站火鼻,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜魁索,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一融撞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧粗蔚,春花似錦尝偎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至当辐,卻和暖如春抖僵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瀑构。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工裆针, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寺晌。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像澡刹,于是被迫代替她去往敵國和親呻征。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,554評論 2 349

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,791評論 25 707
  • 版權(quán)聲明:本文為博主原創(chuàng)文章罢浇,未經(jīng)博主允許不得轉(zhuǎn)載陆赋。微博:厲圣杰微信公眾號:牙鍋子(基本不更)源碼:CircleP...
    牙鍋子閱讀 17,404評論 24 86
  • Android中圓形進度條的應(yīng)用還是挺多的,最近學(xué)習(xí)實現(xiàn)了圓形進度條嚷闭。 思路 要實現(xiàn)圓形進度條攒岛, 首先要畫灰色背景...
    豬爸爸Hulk閱讀 1,045評論 0 8
  • 本人ios初學(xué)者,為自己學(xué)習(xí)方便胞锰,復(fù)制各位大神的學(xué)習(xí)性文章放在自己簡書里灾锯,僅作為自己學(xué)習(xí)方便使用,如果作者疑此行為...
    bu再等閱讀 1,135評論 0 3
  • 《心花路放》里面有一個情節(jié)是這樣的,康小雨喜歡用橘子皮泡水凌那,耿浩把康小雨剩下的橘子瓣丟進啤酒里兼雄,他們在洱海...
    我和遠方的故事閱讀 692評論 0 6