1. 程式人生 > >自定義View之帶進度百分比ProgressBar

自定義View之帶進度百分比ProgressBar

先上幾張自定義所實現的效果圖吧,有興趣的可以繼續往下看

      

實現思路,前四張圖呢在自定義progressbar時沒有加入text文字,文字是在xml佈局時加上去的,最後一張是與progressbar定義在一起的。可以看到有以下幾種情況

1,圖1自定義中未整合文字的圓環顯示,這樣的話需要自己新增文字,做法也很簡單

利用相對佈局,將文字與progressbar進行巢狀,如下:這是整個頁面的佈局檔案,所自定的view為RoundProgressBar

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:gravity="center"/>
    <com.fang.zrf.wifidemo.widget.RoundProgressBar
        android:id="@+id/progress"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_centerInParent="true" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/confirm"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="確定"
            android:layout_alignRight="@+id/input"
            android:layout_marginTop="20dp"/>
        <EditText
            android:id="@+id/input"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/input_current_progress"
            android:padding="10dp"
            android:layout_marginTop="20dp"/>
    </RelativeLayout>


</RelativeLayout>

然後在程式碼中進行處理:當點選確定百分比發生變化後改變文字內容
 mProgress.setCurrentProgress(Integer.valueOf(mEdit.getText().toString()));
                mTv.setText("已完成" + "\n" + mProgress.getPercent() + "%");

這種實現方法有一個好處是文字實現起來相對隨意,不用畫筆實現

2,像圖2這種有填充有圓環的自己感覺使用者體驗不是太好,不如圖4

這種做法忽略掉圓心,做法也很簡單,那就是在畫圓時useCenter傳入一個false,

                    <pre name="code" class="java">     /**
     *@param oval       The bounds of oval used to define the shape and size
     *                   of the arc
     * @param startAngle Starting angle (in degrees) where the arc begins
     * @param sweepAngle Sweep angle (in degrees) measured clockwise,,你要畫的百分比的弧度,
  
     *如果傳入為true,則畫圓時就會包括圓心,其實就相當於用的圓規,如果設定為true,則畫百分比時圓規一腳固定在圓心
     *另一腳沿著圓弧按百分比進行畫弧
    * @param useCenter <span style="color:#3333FF;">If true, include the center of the oval in the arc, and
                        close it if it is being stroked. This will draw a wedge</span>
     * @param paint      The paint used to draw the arc
     */

canvas.drawArc(rectF,0,360*currentProgress/maxProgress,false,paint);
 

3,有了圖2的分析就可以知道,圖3和圖4傳入的useCenter引數為true。

除了包不包含圓心之分,還有一個區分那就是圖1和圖3是空心無填充,圖2和圖4是實心有填充,這個是怎麼設計的呢?

可以看到在畫圓時傳入了一個畫筆的物件paint,可以對畫筆物件進行一些設定,比如

paint.setStyle(Paint.Style.STROKE);//設定為空心

paint.setStyle(Paint.Style.FILL);//設定為實心,在畫時有填充

好了,大致分析了一下幾種情況的不同,接下來看如何自定義View

要想實現這種自定義的view先分析都需要什麼,(直接將圖5考慮進來,如果不需要顯示可以直接注掉)

  • 首先是畫整個圓環(圓環顏色,畫筆物件,圓環寬度)
  • 按百分比進行畫弧(圓弧顏色,最大值,當前值)
  • 考慮是空心還是實心(style)
  • 畫出文字(文字顏色,文字大小,文字是否顯示)
  • 畫時考慮座標

仔細想想,這個View所要畫的也就這些東西了,

自定義view分以下幾步

繼承View(不要怪我囉嗦,說不定真有人會忘....)

public class RoundProgressBar extends View 

既然我們已經知道需要哪些量,那就先進行構造

 private Paint paint;//畫筆物件
    private int ringColor;//圓環color
    private int ringProgressColor;//進度弧度color
    private int textColor;//百分比字型color
    private float textSize;//百分比字型size
    private float ringWidth;//圓環寬度
    private int maxProgress;//進度最大值
    private int currentProgress;//當前進度
    private boolean textIsDisplay;//是否顯示中間進度百分比
    private int styleRes;//進度風格
然後建立欄位的setter和getter方法

構造方法

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

    public RoundProgressBar(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public RoundProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);


        paint = new Paint();
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundProgressBar);
        //獲取自定義屬性和預設值
        ringColor = typedArray.getColor(R.styleable.RoundProgressBar_ringColor, Color.BLACK);
        ringProgressColor = typedArray.getColor(R.styleable.RoundProgressBar_ringProgressColor,Color.RED);
        textColor = typedArray.getColor(R.styleable.RoundProgressBar_textColor,Color.BLUE);
        textSize = typedArray.getDimension(R.styleable.RoundProgressBar_textSize,14);
        textIsDisplay = typedArray.getBoolean(R.styleable.RoundProgressBar_textIsDisplay,true);
        styleRes = typedArray.getInt(R.styleable.RoundProgressBar_style,1);
        typedArray.recycle();
    }

在這裡用到了一個自定義的風格RoundProgressBar的style

在values資料夾下建立一個資原始檔,在該檔案中定義了所需欄位的預設值

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="RoundProgressBar">
        <attr name="ringColor" format="color"/>
        <attr name="ringProgressColor" format="color"/>
        <attr name="textColor" format="color"/>
        <attr name="textSize" format="dimension"/>
        <attr name="ringWidth" format="dimension"/>
        <attr name="maxProgress" format="integer"/>
        <attr name="textIsDisplay" format="boolean"/>

        <attr name="style" >
            <enum name="STROKE"  value="0"/>
            <enum name="FILL" value="1"/>
        </attr>
    </declare-styleable>
</resources>

好了初始化已經完成,接下來就是拿起畫筆開始畫了

覆寫view的onDraw方法

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

在onDraw方法中進行畫view

首先要畫圓環

        int center = getWidth()/2; //獲取到自定義控制元件的寬度,當然這是你在xml檔案中定義的
        int radius = (int)(center - ringWidth/2);//內圓半徑
        paint.setColor(ringColor);//設定圓環顏色
        paint.setStyle(Paint.Style.STROKE);設定是否填充
        paint.setStrokeWidth(ringWidth);//設定圓環寬度
        paint.setAntiAlias(true);//設定是否平滑
        canvas.drawCircle(center,center,radius,paint);畫圓

學過數學的都應該知道吧要想畫圓兩個要素就行,一個是圓心座標,一個就是圓半徑

附上一張說明圖幫助大家理解


當然像這種畫圓方法,你在xml檔案中使用該自定義的控制元件時用padding屬性是沒用的,因為在畫圓時,原點座標是view的左上角,圓心座標是(x軸到圓點的距離,y軸到圓點的距離),要想對控制元件設定padding屬性起作用,必須在畫圓時對半徑進行修改,

 int padding =  Math.min(getPaddingLeft(),getPaddingTop());
        int radius = (int)(center - ringWidth/2 - padding);

圓環畫好後可以開始畫圓弧了

        paint.setStrokeWidth(ringWidth);//圓弧寬度
        paint.setColor(ringProgressColor);//圓弧顏色
    //座標,left,top,right,bottom,參考說明圖,很好理解
     RectF rectF = new RectF(center - radius,center - radius,center + radius,center + radius);
        switch (styleRes){
//這兩種情況一個是空心一個是實心
 case STROKE:
                paint.setStyle(Paint.Style.STROKE);
               //計算出圓弧的長度 = 360 * 當前進度/最大值,至於所傳引數是false還是true的介紹上文已經說明
                canvas.drawArc(rectF,0,360*currentProgress/maxProgress,true,paint);
//                canvas.drawArc(rectF,0,360*currentProgress/maxProgress,false,paint);

                break;
            case FILL:
                paint.setStyle(Paint.Style.FILL);
                if (currentProgress != 0){
                    //canvas.drawArc(rectF,0,360*currentProgress/maxProgress,false,paint);
<pre name="code" class="java">                 canvas.drawArc(rectF,0,360*currentProgress/maxProgress,false,paint);
 } break; }

圓弧畫好後可以開始寫文字了,文字的話應該簡單的多了

       paint.setStrokeWidth(0);
        paint.setColor(textColor);//文字顏色
        paint.setTextSize(textSize);//文字字型大小
        paint.setTypeface(Typeface.DEFAULT_BOLD);//typeface
        percent = (int)(((float)currentProgress/(float) maxProgress)*100);//計算 百分比
        float textWidth = paint.measureText( percent + "%");//測量文字的寬度
        Paint.FontMetrics textHeigh = paint.getFontMetrics();//為了使文字居中,我們要根據文字的寬高來獲取座標
        float height = (float)Math.ceil(textHeigh.descent - textHeigh.top);//獲取到文字的高度
        if (textIsDisplay && percent != 0 && styleRes == STROKE){//如果是空心圓且百分比不為0,且設定的為顯示,則顯示
//橫座標為center-textWidth/2 :外圓環的半徑減去文字的寬度,
//縱座標為center+height/2:外圓環的半徑 加上文字的高度
            canvas.drawText(percent + "%",center - textWidth/2 ,center + height/2  ,paint);
        }
至此,一個帶進度百分比的progress已經自定義完成
現在想想,曾經面試有人問自定義view要實現哪些方法,不就是構造方法和ondraw麼。。

附上demo下載地址