Android-自定義view之圓(選擇程度圓以及進度圓)詳解

很多時候我們需要用一個優美好看的圓之類的來表示進度或者選擇程度,但是android自帶的控件一般很難滿足我們的需求,這時候就到自定義view登場啦!


文章結構:1.解析一個手動選擇的程度圓的自定義view制作 2.解析一個圓環中的圓弧轉動來表示進度的進度圓 (這兩個例子已經幫各位寫好調大小的方法了,復制即可使用)【接下來一段時間,本博主還會持續更新一系列的自定義view,敬請關注。】


先上圖 可以看到上面的就是點擊選擇程度的圓,下面就是用圓弧旋轉表示進度的進度圓
這里寫圖片描述


一、一個手動選擇的程度圓的自定義view制作

1.先給出代碼,在代碼里面解析


import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;

import com.demo.myview.R;

/**
 * Created by ${符柱成} on 2016/8/20.
 */
public class ManualCircle extends View {
    private Paint circlePaint, arcPaint, textPaint;           //畫圓的筆,畫弧線的筆,畫圓中文字的筆
    private RectF rectF;                            //畫弧線--弧線根據矩形生成
    //弧線度數變量
    int i = 0;
    /**
     * 圓環的寬度
     */
    private int mCircleWidth;
    //中心圓的顏色
    private int centerCircleColor;
    //圓環的顏色
    private int ringColor;
    //圓文字的顏色
    private int textColor;

    public ManualCircle(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomProgressBar);//將獲取的屬性轉化為我們最先設好的屬性
        int n = typedArray.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
                case R.styleable.CustomProgressBar_circleWidth :
                    mCircleWidth = (int) typedArray.getDimension(attr, 0);
                    break;
                case R.styleable.CustomProgressBar_centercircleColor :
                    centerCircleColor=typedArray.getColor(attr,0);       //默認黃色
                    break;
                case R.styleable.CustomProgressBar_ringColor :
                    ringColor=typedArray.getColor(attr,0);          //默認綠色
                    break;
                case R.styleable.CustomProgressBar_textColor :
                    textColor=typedArray.getColor(attr,0);          //默認黑色
                    break;
            }
        }
        typedArray.recycle();           //回收屬性對象
        initPaint();
    }


    private void initPaint() {
        //畫文字
        textPaint = new TextPaint(0);
        textPaint.setColor(textColor);
        textPaint.setTextSize(80);
        textPaint.setTextAlign(Paint.Align.CENTER);
        //實心圓畫筆
        circlePaint = new Paint();
        circlePaint.setColor(centerCircleColor);
        //畫弧線的畫筆
        arcPaint = new Paint();
        arcPaint.setStyle(Paint.Style.STROKE);    //設置不填充中間,樣式為描邊
        arcPaint.setColor(ringColor);
        //是根據矩形來畫弧線的
        rectF = new RectF();


    }


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

        //這是圓心的坐標,這個是在xml設置引用的這個布局控件的大小的中心點到這個布局控件的最左邊
        float xy = getWidth() / 2;
        //圓圈的半徑 這里是100
        float radius = xy - mCircleWidth;       //改半徑會造成聯動的,因為圍繞著mCircleWidth這個變量去開展繪制,要理解就要畫圖,大家拿起手上的紙畫一下就好
        //設置圓弧              //注意先確定圓弧的寬度,然后根據得到這個布局的中心點,然后進行演算,得到半徑。然后確定一個矩形來畫圓弧。圓弧
        rectF.left = xy - radius - mCircleWidth / 2;
        rectF.top = xy - radius - mCircleWidth / 2;
        rectF.right = xy + radius + mCircleWidth / 2;
        rectF.bottom = xy + radius + mCircleWidth / 2;
        arcPaint.setStrokeWidth(mCircleWidth);

        //畫里面的圓
        canvas.drawCircle(xy, xy, radius, circlePaint);
        //畫弧線
        int progress=i*10/36;
        canvas.drawArc(rectF, 270, i, false, arcPaint);
        //顯示度數與提示
        canvas.drawText(String.valueOf(progress)+"%", xy, xy, textPaint);
    }

    /**
     * 公開一個方法,可以更新弧線的度數,也就是選擇程度啦,設置為單擊一次就增加10%
     */
    public void add() {
        //超過360度就還原到10度
        if (i >= 360) {
            i = 36;
            postInvalidate();           //通知onDraw重新繪制
        } else {
            this.i += 36;
            postInvalidate();
        }
    }
}


2.然后給出屬性文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 手動選擇的程度圓-->
    <declare-styleable name="CustomProgressBar">
        <attr name="circleWidth" format="dimension" />
        <attr name="ringColor" format="color" />
        <attr name="centercircleColor" format="color" />
        <attr name="textColor" format="color" />
    </declare-styleable>

<!--用弧度變化表示進度的程度圓,一會就不貼這個的代碼了-->

    <declare-styleable name="AutomaticCircleProgress">
        <attr name="automaticCircleWidth" format="dimension" />
        <attr name="automaticBottomRingColor" format="color" />
        <attr name="automaticDrawRingColor" format="color" />
        <attr name="automaticCentercircleColor" format="color" />
        <attr name="automaticTextColor" format="color" />
    </declare-styleable>
</resources>


3.接下來就是給出在xml中的調用啦:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.demo.myview.progressCircle.ProgressCircleActivity">
<!-- 手動選擇的程度圓-->
    <com.demo.myview.progressCircle.ManualCircle
        android:id="@+id/myview"
        android:layout_width="160dp"
        android:layout_height="160dp"
        android:layout_centerInParent="true"
        app:centercircleColor="#FFFF00"
        app:circleWidth="11dp"
        app:ringColor="#008000"
        app:textColor="#000000"></com.demo.myview.progressCircle.ManualCircle>
<!--用弧度變化表示進度的程度圓,一會就不貼這個的代碼了-->

    <com.demo.myview.progressCircle.AutomaticCircle
        android:id="@+id/automaticCircle"
        android:layout_width="160dp"
        android:layout_height="160dp"
        app:automaticBottomRingColor="#000000"
        app:automaticCentercircleColor="#aaa"
        app:automaticCircleWidth="20dp"
        app:automaticDrawRingColor="#FFFFFF"
        app:automaticTextColor="#FFFFFF" />

</LinearLayout>


然后就是為選擇程度的圓添加我們剛剛在view里面暴露的接口方法

public class ProgressCircleActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_progress_circle);
        //<!-- 手動選擇的程度圓-->

        final ManualCircle myView = (ManualCircle) findViewById(R.id.myview);
        myView.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                myView.add();
            }
        });
//<!--用弧度變化表示進度的程度圓,一會就不貼這個的代碼了-->

        final AutomaticCircle automaticCircle=(AutomaticCircle)findViewById(R.id.automaticCircle);
        automaticCircle.setScore(80);
    }
}

二、用弧度變化表示進度的程度圓

1.先給出view的代碼,在代碼里面解析

package com.demo.myview.progressCircle;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;

import com.demo.myview.R;

/**
 * Created by ${符柱成} on 2016/8/21.
 */
public class AutomaticCircle extends View {
    private int mScore;      //方法傳進來的表示進度
    private Paint mBlackPaint, mWhitePaint, mCirclePaint, mTextPaint;        //白色圓弧與黑色圓弧
    /**
     * 圓環的寬度
     */
    private int mCircleWidth;
    //圓弧的矩形
    private RectF mRectF;

    //白弧線度數變量
    int i = 0;
    //根據進度來白圓掃
    int count;
    //中心圓的顏色
    private int centerCircleColor;
    //底圓環的顏色
    private int bottomRingColor;
    //旋轉圓環的顏色
    private int drawRingColor;
    //圓文字的顏色
    private int textColor;


    public AutomaticCircle(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AutomaticCircleProgress);//將獲取的屬性轉化為我們最先設好的屬性
        int n = typedArray.getIndexCount();
        for (int i = 0; i < n; i++) {
        //屬性文件在上面已經給出
            int attr = typedArray.getIndex(i);
            switch (attr) {
                case R.styleable.AutomaticCircleProgress_automaticCircleWidth:
                    mCircleWidth = (int) typedArray.getDimension(attr, 0);
                    break;
                case R.styleable.AutomaticCircleProgress_automaticCentercircleColor:
                    centerCircleColor = typedArray.getColor(attr, 0);
                    Log.e("centerColor", String.valueOf(centerCircleColor));
                    break;
                case R.styleable.AutomaticCircleProgress_automaticBottomRingColor:
                    bottomRingColor =  typedArray.getColor(attr, 0);
                    Log.e("centerColor", String.valueOf(bottomRingColor));
                    break;
                case R.styleable.AutomaticCircleProgress_automaticDrawRingColor:
                    drawRingColor =  typedArray.getColor(attr, 0);
                    break;
                case R.styleable.AutomaticCircleProgress_automaticTextColor:
                    textColor = typedArray.getColor(attr, 0);
                    break;
            }
        }
        initPaint();
    }

    private void initPaint() {
        //初始化中心圓的黑色筆
        mCirclePaint = new Paint();
        //設置抗鋸齒,優化繪制效果的精細度
        mCirclePaint.setAntiAlias(true);
        //設置圖像抖動處理,也是用于優化圖像的顯示效果
        mCirclePaint.setDither(true);
        //設置畫筆的顏色
        mCirclePaint.setColor(centerCircleColor);

        //初始圓弧黑色筆
        mBlackPaint = new Paint();
        //設置抗鋸齒,優化繪制效果的精細度
        mBlackPaint.setAntiAlias(true);
        //設置圖像抖動處理,也是用于優化圖像的顯示效果
        mBlackPaint.setDither(true);
        //設置畫筆的顏色
        mBlackPaint.setColor(bottomRingColor);
        //設置畫筆的風格為空心
        mBlackPaint.setStyle(Paint.Style.STROKE);
        //設置“空心”的外框寬度為2dp
        mBlackPaint.setStrokeWidth(mCircleWidth);

        //初始白色筆
        mWhitePaint = new Paint();
        mWhitePaint.setAntiAlias(true);
        mWhitePaint.setStyle(Paint.Style.STROKE);
        mWhitePaint.setStrokeWidth(mCircleWidth);
        mWhitePaint.setDither(true);
        mWhitePaint.setColor(drawRingColor);

        //文本的筆
        mTextPaint = new Paint();
        //設置文本的字號大小
        mTextPaint.setTextSize(40);
        mTextPaint.setDither(true);
        //設置文本的對其方式為水平居中
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        mTextPaint.setColor(textColor);


        //獲取該view的視圖樹觀察者并添加繪制變化監聽者
        //實現有繪制變化時的回調方法
        this.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                //2.開啟子線程對繪制用到的數據進行修改
                new DrawThread();
                getViewTreeObserver().removeOnPreDrawListener(this);
                return false;
            }
        });
    }

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

        //這是圓心的坐標,這個是在xml設置引用的這個布局控件的大小的中心點到這個布局控件的最左邊
        float xy = getWidth() / 2;
        //圓圈的半徑
        float radius = xy - mCircleWidth;       //改半徑會造成聯動的,因為圍繞著mCircleWidth這個變量去開展繪制

        //初始化圓弧所需條件(及設置圓弧的外接矩形的四邊)
        mRectF = new RectF();
        mRectF.set(xy - radius - mCircleWidth / 2, xy - radius - mCircleWidth / 2, xy + radius + mCircleWidth / 2, xy + radius + mCircleWidth / 2);

        //畫里面的灰色圓
        canvas.drawCircle(xy, xy, radius, mCirclePaint);


        //繪制弧形
        //黑筆畫的是一個整圓所有從哪里開始都一樣
        canvas.drawArc(mRectF, 0, 360, false, mBlackPaint);
        //白筆之所以從-90度開始,是因為0度其實使我們的3點鐘的位置,所以-90才是我們的0點的位置
        canvas.drawArc(mRectF, -90, i, false, mWhitePaint);
        //繪制文本
        canvas.drawText(count + "", xy, xy, mTextPaint);


    }
    //暴露一個方法給外部調用來調整進度
    public void setScore(int score) {
        this.mScore = score;
    }
    //開啟子線程,并通過繪制監聽實時更新繪制數據
    public class DrawThread implements Runnable {
        private final Thread mDrawThread;
        private int statek;

        public DrawThread() {
            mDrawThread = new Thread(this);
            mDrawThread.start();
        }

        @Override
        public void run() {
            while (true) {
                switch (statek) {
                    case 0://給一點點緩沖的時間
                        try {
                            Thread.sleep(200);
                            statek = 1;
                        } catch (InterruptedException e) {

                        }
                        break;
                    case 1:
                        try {//更新顯示的數據
                            Thread.sleep(20);
                            i += 3.6f;
                            count++;
                            postInvalidate();       //每count加1就去刷新onDraw來刷新圖
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        break;
                }
                if (count >= mScore)//滿足該條件就結束循環
                    break;
            }

        }
    }
}

恩,屬性文件和調用的代碼都在上一個例子里面了,這里就不再貼了。


好了,自定義view圓已經講解完畢,歡迎指出錯誤,共同學習。接下來一段時間還會更新一系列的自定義view,敬請關注。

轉載請注明:【JackFrost的博客】

更多精彩的內容,可以訪問JackFrost的博客

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,427評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,380評論 2 379

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,712評論 25 708
  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,162評論 4 61
  • 昨天回到武漢,直接去了豐兄公司,本來說請他吃飯,結果又是他盛情招待,還介紹幾個朋友認識,席間他又在幫我拉客戶,真的...
    肖博文閱讀 311評論 1 0
  • 正值碧玉年華,半月后便虛歲十八,都說十八歲的姑娘一朵花。人總會長大,而突然長大的一瞬間也是各式各樣。有的人會...
    uhke閱讀 673評論 3 2
  • 再次開始,隨心涂畫,記錄生活!
    隨心_9862閱讀 138評論 0 2