聲明:作者原創(chuàng),轉(zhuǎn)載注明出處。
作者:帥氣陳吃蘋果
一、Android控件架構(gòu)
開發(fā)過程中,當(dāng)Android原生控件無法滿足項(xiàng)目需求時(shí),就需要我們自己自定義View來實(shí)現(xiàn)了。
Android中的每個(gè)控件都會(huì)在界面上占據(jù)一個(gè)矩形區(qū)域,控件大致分成兩類,ViewGroup控件和View控件。ViewGroup控件往往作為容器,它可以包含多個(gè)View控件,并管理被其包含的子控件。通過ViewGroup,整個(gè)界面上的控件形成了一個(gè)樹形結(jié)構(gòu),如下圖,上層控件負(fù)責(zé)下層子控件的測(cè)量和繪制,并傳遞交互事件。
Android控件樹:
二、自定義View
View類是Android中的一個(gè)超類,ViewGroup類也是繼承自View類。View中通常有下面這些比較重要的回調(diào)方法:
-
onFinishInflate():從XML加載組件后回調(diào); -
onSizeChanged():組件大小改變時(shí)回調(diào); -
onMeasure():對(duì)組件的大小進(jìn)行測(cè)量; -
onLayout():對(duì)子控件進(jìn)行排列,確定子控件的位置; -
onDraw():繪制子控件的內(nèi)容; -
onTouchEvent():監(jiān)聽組件的觸摸事件;
其中,最常用的是onMeasure()、onLayout()、onDraw()和onTouchEvent()。
通常情況下,自定義控件可以分為下面三類:
- 繼承現(xiàn)有控件,對(duì)其進(jìn)行擴(kuò)展;
- 組合不同的控件來實(shí)現(xiàn)新的控件;
- 重寫View實(shí)現(xiàn)全新的控件;
當(dāng)我們想要實(shí)現(xiàn)一個(gè)自定義View時(shí),需要思考它是屬于哪一類的自定義控件,并且思考實(shí)現(xiàn)這樣的控件,需要用到View中的哪些回調(diào)方法。
比如,當(dāng)你只是想改變TextView的外觀時(shí),它就是屬于第一類,那么你可以新建一個(gè)類,繼承TextView,并重寫onDraw()和onMeasure()等方法。
在實(shí)現(xiàn)自定義控件的時(shí)候,我們往往需要對(duì)控件進(jìn)行測(cè)量、繪制、和布局等操作。
我們可以把自定義控件這個(gè)過程想象成畫畫,當(dāng)我們畫一個(gè)東西時(shí),要想:這個(gè)東西要畫多大,這個(gè)東西要怎么畫,這個(gè)東西要畫在哪個(gè)位置。
(一)View的測(cè)量
Android系統(tǒng)給我們提供了一個(gè)專門幫助我們測(cè)量View的類,MeasureSpec,它是一個(gè)32位的int值,其中高2位為測(cè)量的模式,低30位為測(cè)量的大小。測(cè)量模式又分為EXACTLY、AT_MOST、UNSPECIFIED。
- EXACTLY:
精確值模式,當(dāng)我們將控件的layout_width屬性或layout_height屬性指定為具體數(shù)值或者match_parent時(shí),就代表著該控件的測(cè)量模式是EXACTLY模式。 - AT_MOST:
最大值模式,當(dāng)控件的layout_width屬性或layout_height屬性指定為wrap_content時(shí),控件的大小就會(huì)隨著內(nèi)容的變化而變化,內(nèi)容有多大,它就占據(jù)多大空間。 - UNSPECIFIED:
不指定測(cè)量模式,View想多大就多大,常用于自定義View,
View類默認(rèn)的onMeasure()方法只支持EXACTLY模式,所以在實(shí)現(xiàn)自定義控件的時(shí)候,如果沒有重寫onMeasure()方法,那么在使用的時(shí)候必須指定控件的具體數(shù)值,而不能指定為wrap_content,否則會(huì)出現(xiàn)問題,后面的例子中會(huì)具體介紹出現(xiàn)的問題。
(二)View的繪制
既然是畫畫,那么就需要用到一些工具,比如畫布、畫筆、顏料等,而在Android中,每一個(gè)View都有一個(gè)用于繪圖的畫布,即Canvas,用于繪制圖形的畫筆是Paint,而顏料則是我們自己定義的一些顏色屬性,只要給畫筆設(shè)置顏色屬性,就相當(dāng)于擁有任意顏色任意數(shù)量的畫筆了。
Canvas的常用屬性:
1)填充顏色
drawARGB(int a, int r, int g, int b)
drawColor(int color)
drawRGB(int r, int g, int b)
drawColor(int color, PorterDuff.Mode mode)
2)繪制幾何圖形
canvas.drawArc() :繪制一個(gè)扇形或者一段弧形
canvas.drawCircle():繪制一個(gè)圓形
canvas.drawOval():繪制一個(gè)橢圓
canvas.drawLine():繪制一條線
canvas.drawPoint():繪制一個(gè)點(diǎn)
canvas.drawRect():繪制一個(gè)矩形
canvas.drawRoundRect():繪制一個(gè)圓角矩形
canvas.drawVertices():繪制一個(gè)頂點(diǎn)
cnavas.drawPath():繪制一條路徑
3)圖片
canvas.drawBitmap() :繪制位圖,裝載畫布
canvas.drawPicture():繪制圖片
4)文本
canvas.drawText():繪制文字
Paint的常用屬性:
Paint.setAntiAlias():抗鋸齒
Paint.setStyle():設(shè)置畫筆風(fēng)格
Paint.setStrokeWidth():設(shè)置畫筆寬度
Paint.setColor():設(shè)置畫筆顏色
Paint.setTextSize():設(shè)置畫筆繪制文本的文字大小
三、實(shí)例
介紹了自定義View的分類、流程、常用回調(diào)方法以及需要用到的工具,接下來,偉大的畫家要開始畫畫了。
我們要實(shí)現(xiàn)的是一個(gè)圓形進(jìn)度條控件,中間的文本動(dòng)態(tài)顯示當(dāng)前進(jìn)度值,如圖:
按照前面說的思路來做,首先,這樣一個(gè)控件,好像沒有原生控件可以直接利用,并且它不是ViewGroup,所以,它屬于第3類自定義控件。
接下來思考可能要用到的回調(diào)方法,onDraw()方法是必須的,因?yàn)檫@是一個(gè)全新的控件。onMeasure()需要用到嗎?現(xiàn)在可能還不知道,等到具體實(shí)現(xiàn)的時(shí)候或許就知道了。
然后分析應(yīng)該怎么樣繪制這樣一個(gè)控件。它由一個(gè)圓環(huán)、一個(gè)圓弧、一段文本組成,那么很明顯了,需要三個(gè)步驟:
- 繪制圓環(huán);
- 繪制圓?。匆淹瓿蛇M(jìn)度的部分);
- 繪制文本;
可以動(dòng)手編碼了。新建一個(gè)類CircleProgressView 繼承自View,實(shí)現(xiàn)構(gòu)造方法,并重寫onDraw()、onMeasure()方法,如下:
public class CircleProgressView extends View {
public CircleProgressView(Context context) {
super(context);
}
public CircleProgressView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public CircleProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
}
}
這個(gè)控件擁有一些屬性,比如圓環(huán)的寬度、圓環(huán)的顏色、進(jìn)度條的顏色、文字大小、文字顏色、當(dāng)前進(jìn)度值、控件的寬高等,還需要用到畫筆,所以可以在這個(gè)類中添加下列屬性:
//圓環(huán)的寬度
private int ringWidth;
//圓環(huán)填充顏色
private int ringColor;
//進(jìn)度條填充顏色
private int progressColor;
//文字大小
private int textSize;
//文字顏色
private int textColor;
//畫筆
private Paint mPaint;
//當(dāng)前進(jìn)度值
private int progressSize;
//控件本身的寬度
private int mWidth;
雖然有了這些屬性,但是當(dāng)我們?cè)谑褂眠@個(gè)控件的時(shí)候,該怎么樣給這些屬性賦值呢?這就需要用到構(gòu)造方法了。三個(gè)構(gòu)造方法的使用如下:
/**
* 當(dāng)在java代碼中直接new一個(gè)控件實(shí)例的時(shí)候,調(diào)用此構(gòu)造方法
*/
public CircleProgressView(Context context) {
super(context);
}
/**
* 當(dāng)在XML文件中直接使用該控件的時(shí)候,
* 并且該控件由自定義屬性的時(shí)候,調(diào)用此構(gòu)造方法
*/
public CircleProgressView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
/**
* 系統(tǒng)默認(rèn)只調(diào)用前兩個(gè)構(gòu)造方法,
* 此方法通常是我們?cè)谇皟蓚€(gè)構(gòu)造方法中調(diào)用,
* 用于獲取自定義屬性的值
*/
public CircleProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
要使用自定義屬性,需要在res資源目錄的values目錄下創(chuàng)建一個(gè)attrs.xml的屬性定義文件,并添加屬性代碼:
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CircleProgressView" >
<attr name="ringWidth" format="dimension"/>
<attr name="ringColor" format="color" />
<attr name="progressColor" format="color"/>
<attr name="textSize" format="dimension"/>
<attr name="textColor" format="color"/>
<attr name="progressSize" format="integer" />
</declare-styleable>
</resources>
自定義屬性設(shè)置好了,像前面說的,可以在第三個(gè)構(gòu)造方法中獲取這些自定義屬性的值,由于不需要在java代碼中實(shí)例化創(chuàng)建該控件,可以在前兩個(gè)構(gòu)造方法中調(diào)用第三個(gè)構(gòu)造方法。通常也把一些初始化操作放在構(gòu)造方法中,比如我們這里用到的畫筆Paint的初始化,代碼如下:
public CircleProgressView(Context context) {
this(context,null);
}
public CircleProgressView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public CircleProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setAntiAlias(true);
//獲取屬性值
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.CircleProgressView);
//第二個(gè)參數(shù)是當(dāng)我們沒有給這個(gè)控件對(duì)應(yīng)的屬性賦值時(shí)采用的默認(rèn)值
ringWidth = (int) ta.getDimension(R.styleable.CircleProgressView_ringWidth,20);
ringColor = ta.getColor(R.styleable.CircleProgressView_ringColor, Color.GRAY);
progressColor = ta.getColor(R.styleable.CircleProgressView_progressColor,Color.BLUE);
textSize = (int) ta.getDimension(R.styleable.CircleProgressView_textSize,60);
textColor = ta.getColor(R.styleable.CircleProgressView_textColor,Color.BLACK);
progressSize = ta.getInteger(R.styleable.CircleProgressView_progressSize,60);
//回收TypedArray
ta.recycle();
}
控件本身的寬度可以在onMeasure()方法中獲取到:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getMeasuredWidth();
}
準(zhǔn)備工作做好了,接下來開始畫畫了。
繪制圓環(huán):
繪制圓環(huán)可以看成是繪制一個(gè)邊框?qū)挾容^大的空心圓,首先獲取到圓心的坐標(biāo)和半徑,設(shè)置畫筆的屬性后,繪制圓:
//獲取圓心坐標(biāo)及半徑
float circleX = mWidth / 2;
float circleY = mWidth / 2;
float radius = mWidth / 2 - ringWidth / 2;
//繪制圓環(huán)
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(ringWidth);
mPaint.setColor(ringColor);
canvas.drawCircle(circleX,circleY,radius,mPaint);
繪制圓?。?/strong>
//繪制圓弧,填充進(jìn)度
//RectF用于構(gòu)造一個(gè)矩形區(qū)域,作為傳入的橢圓對(duì)象
RectF oval = new RectF(ringWidth / 2,ringWidth / 2,mWidth - ringWidth / 2,mWidth - ringWidth / 2);
mPaint.setColor(progressColor);
//drawArc()方法參數(shù):
//1、圓弧所在的橢圓對(duì)象
//2、圓弧的起始角度
//3、圓弧的角度
//4、是否顯示半徑連線
//5、繪制時(shí)采用的畫筆
canvas.drawArc(oval,0,progressSize * 360 / 100,false,mPaint);
繪制文本:
//繪制文本
String progressText = progressSize + "%";
//設(shè)置畫筆顏色和文字大小
mPaint.setColor(textColor);
mPaint.setTextSize(textSize);
//重置畫筆寬度,因?yàn)榍懊胬L制圓環(huán)和圓弧時(shí)用到的畫筆寬度不一樣
mPaint.setStrokeWidth(0);
//構(gòu)造一個(gè)矩形區(qū)域,用于放置文本
Rect bound = new Rect();
mPaint.getTextBounds(progressText,0,progressText.length(),bound);
canvas.drawText(progressText,mWidth / 2 - bound.width() / 2,mWidth / 2 + bound.height() / 2,mPaint);
由于進(jìn)度值是動(dòng)態(tài)的,所以我們需要提供一個(gè)方法,用于傳入進(jìn)度值progressSize,在CircleProgressView類中添加如下方法:
/**
* 獲取進(jìn)度值
* @return
*/
public int getProgressSize() {
return progressSize;
}
/**
* 設(shè)置進(jìn)度值
* @param progressSize
*/
public void setProgressSize(int progressSize) {
this.progressSize = progressSize;
}
自定義控件的實(shí)現(xiàn)工作已經(jīng)完成了,接下來是如何使用我們的控件。
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<edu.sqchen.circleprogressview.CircleProgressView
android:id="@+id/circle_progress_view"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_centerInParent="true"
app:ringColor="@android:color/darker_gray"
app:progressColor="@color/blue"
app:ringWidth="10dp"
app:textColor="@color/blue"
/>
</RelativeLayout>
MainActivity.class:
public class MainActivity extends AppCompatActivity {
//自定義控件
private CircleProgressView mProgressView;
//已完成進(jìn)度
private int totalProgress;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mProgressView = (CircleProgressView) findViewById(R.id.circle_progress_view);
//已完成70%
totalProgress = 70;
//創(chuàng)建一個(gè)子線程,在子線程中做耗時(shí)操作
new Thread(new Runnable() {
@Override
public void run() {
//設(shè)置進(jìn)度值從0開始變化
mProgressView.setProgressSize(0);
for(int i = 0; i < totalProgress; i++) {
mProgressView.setProgressSize(i + 1);
SystemClock.sleep(30);
//在子線程中刷新、重繪控件
mProgressView.postInvalidate();
}
}
}).start();
}
}
自定義圓形進(jìn)度條已經(jīng)實(shí)現(xiàn)了,效果如下:
[圖片上傳失敗...(image-ae6aeb-1513606785650)]
前面我們?cè)?code>activity_main.xml中給控件的寬高設(shè)置為具體指,那么假如要設(shè)置為wrap_content呢?修改activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<edu.sqchen.circleprogressview.CircleProgressView
android:id="@+id/circle_progress_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
app:ringColor="@android:color/darker_gray"
app:progressColor="@color/blue"
app:ringWidth="10dp"
app:textColor="@color/blue"
/>
</RelativeLayout>
效果:
可以看到,控件的大小占據(jù)了整個(gè)屏幕,顯然不是我們想要的效果。
原因在于,我們雖然重寫了onMeasure()方法,但是我們沒有對(duì)測(cè)量模式AT_MOST作處理,它就會(huì)變成這樣的效果,這也是前面所說的可能出現(xiàn)的問題,解決方式自然是重寫onMeasure()方法,在里面對(duì)寬高指定為wrap_content時(shí)的處理,代碼如下:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
mWidth = getMeasuredWidth();
}
/**
* 對(duì)寬度進(jìn)行判斷
* @param widthMeasureSpec
* @return
*/
private int measureWidth(int widthMeasureSpec) {
int resultWidth = 0;
//獲取設(shè)置的測(cè)量模式和大小
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
//如果是精確值模式,則寬度等于用戶設(shè)置的寬度
if(specMode == MeasureSpec.EXACTLY) {
resultWidth = specSize;
} else {
//否則,設(shè)置默認(rèn)值為400個(gè)像素,如果是最大值模式,則取用戶設(shè)置的值和默認(rèn)值中較小的一個(gè)
resultWidth = 400;
if(specMode == MeasureSpec.AT_MOST) {
resultWidth = Math.min(resultWidth,specSize);
}
}
return resultWidth;
}
/**
* 對(duì)高度進(jìn)行判斷
* @param heightMeasureSpec
* @return
*/
private int measureHeight(int heightMeasureSpec) {
int resultHeight = 0;
int specMode = MeasureSpec.getMode(heightMeasureSpec);
int specSize = MeasureSpec.getSize(heightMeasureSpec);
if(specMode == MeasureSpec.EXACTLY) {
resultHeight = specSize;
} else {
resultHeight = 400;
if(specMode == MeasureSpec.AT_MOST) {
resultHeight = Math.min(resultHeight,specSize);
}
}
return resultHeight;
}
可以看到,我們對(duì)傳遞進(jìn)來的寬高進(jìn)行測(cè)量模式的判斷,如果是精確值模式,則采用用戶設(shè)置的具體寬度,否則判斷是否是最大值模式,則取用戶設(shè)置的值(即wrap_content)和默認(rèn)值400像素中較小的那個(gè)值。
需要注意的是,java代碼中設(shè)置的大小單位是像素,而XML文件中設(shè)置的大小單位是dp,根據(jù)手機(jī)分辨率不同而有所差異,可將像素轉(zhuǎn)換成dp,則可自適應(yīng)不同屏幕,統(tǒng)一大小。
現(xiàn)在再回過頭看前面的繪制流程、回調(diào)方法、繪制工具,對(duì)自定義View的整個(gè)過程就比較熟悉了。
CircleProgressView.class完整代碼:
package edu.sqchen.circleprogressview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by Administrator on 2017/6/1.
*/
public class CircleProgressView extends View {
//圓環(huán)的寬度
private int ringWidth;
//圓環(huán)填充顏色
private int ringColor;
//進(jìn)度條填充顏色
private int progressColor;
//文字大小
private int textSize;
//文字顏色
private int textColor;
//畫筆
private Paint mPaint;
//當(dāng)前進(jìn)度值
private int progressSize;
//控件本身的寬度
private int mWidth;
/**
*
* @param context
*/
public CircleProgressView(Context context) {
this(context,null);
}
public CircleProgressView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public CircleProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setAntiAlias(true);
//獲取屬性值
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.CircleProgressView);
//第二個(gè)參數(shù)是當(dāng)我們沒有給這個(gè)控件對(duì)應(yīng)的屬性賦值時(shí)采用的默認(rèn)值
ringWidth = (int) ta.getDimension(R.styleable.CircleProgressView_ringWidth,20);
ringColor = ta.getColor(R.styleable.CircleProgressView_ringColor, Color.GRAY);
progressColor = ta.getColor(R.styleable.CircleProgressView_progressColor,Color.BLUE);
textSize = (int) ta.getDimension(R.styleable.CircleProgressView_textSize,60);
textColor = ta.getColor(R.styleable.CircleProgressView_textColor,Color.BLACK);
progressSize = ta.getInteger(R.styleable.CircleProgressView_progressSize,60);
//回收TypedArray
ta.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
mWidth = getMeasuredWidth();
}
/**
* 對(duì)寬度進(jìn)行判斷
* @param widthMeasureSpec
* @return
*/
private int measureWidth(int widthMeasureSpec) {
int resultWidth = 0;
//獲取設(shè)置的測(cè)量模式和大小
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
//如果是精確值模式,則寬度等于用戶設(shè)置的寬度
if(specMode == MeasureSpec.EXACTLY) {
resultWidth = specSize;
} else {
//否則,設(shè)置默認(rèn)值為400個(gè)像素,如果是最大值模式,則取用戶設(shè)置的值和默認(rèn)值中較小的一個(gè)
resultWidth = 400;
if(specMode == MeasureSpec.AT_MOST) {
resultWidth = Math.min(resultWidth,specSize);
}
}
return resultWidth;
}
/**
* 對(duì)高度進(jìn)行判斷
* @param heightMeasureSpec
* @return
*/
private int measureHeight(int heightMeasureSpec) {
int resultHeight = 0;
int specMode = MeasureSpec.getMode(heightMeasureSpec);
int specSize = MeasureSpec.getSize(heightMeasureSpec);
if(specMode == MeasureSpec.EXACTLY) {
resultHeight = specSize;
} else {
resultHeight = 400;
if(specMode == MeasureSpec.AT_MOST) {
resultHeight = Math.min(resultHeight,specSize);
}
}
return resultHeight;
}
@Override
protected void onDraw(Canvas canvas) {
//獲取圓心坐標(biāo)及半徑
float circleX = mWidth / 2;
float circleY = mWidth / 2;
float radius = mWidth / 2 - ringWidth / 2;
//繪制圓環(huán)
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(ringWidth);
mPaint.setColor(ringColor);
canvas.drawCircle(circleX,circleY,radius,mPaint);
//繪制圓弧,填充進(jìn)度
//RectF用于構(gòu)造一個(gè)矩形區(qū)域,作為傳入的橢圓對(duì)象
RectF oval = new RectF(ringWidth / 2,ringWidth / 2,mWidth - ringWidth / 2,mWidth - ringWidth / 2);
mPaint.setColor(progressColor);
//drawArc()方法參數(shù):
//1、圓弧所在的橢圓對(duì)象
//2、圓弧的起始角度
//3、圓弧的角度
//4、是否顯示半徑連線
//5、繪制時(shí)采用的畫筆
canvas.drawArc(oval,0,progressSize * 360 / 100,false,mPaint);
//繪制文本
String progressText = progressSize + "%";
//設(shè)置畫筆顏色和文字大小
mPaint.setColor(textColor);
mPaint.setTextSize(textSize);
//重置畫筆寬度,因?yàn)榍懊胬L制圓環(huán)和圓弧時(shí)用到的畫筆寬度不一樣
mPaint.setStrokeWidth(0);
//構(gòu)造一個(gè)矩形區(qū)域,用于放置文本
Rect bound = new Rect();
mPaint.getTextBounds(progressText,0,progressText.length(),bound);
canvas.drawText(progressText,mWidth / 2 - bound.width() / 2,mWidth / 2 + bound.height() / 2,mPaint);
}
/**
* 獲取進(jìn)度值
* @return
*/
public int getProgressSize() {
return progressSize;
}
/**
* 設(shè)置進(jìn)度值
* @param progressSize
*/
public void setProgressSize(int progressSize) {
this.progressSize = progressSize;
}
}
源碼下載地址:Github下載