Android自定義View實(shí)現(xiàn)圓弧進(jìn)度的效果
前言
Android開發(fā)中,常常自定義View實(shí)現(xiàn)自己想要的效果,當(dāng)然自定義View也是Android開發(fā)中比較難的部分,涉及到的知識(shí)有Canvas(畫布),Paint(畫筆)等,自定義控件分為三種:一是直接繼承自View,完全的自定義;二是在原有控件的基礎(chǔ)上進(jìn)行改造,達(dá)到自己想要的效果;還有一種就是自定義組合控件,將已有的控件根據(jù)自己的需要進(jìn)行組合實(shí)現(xiàn)的效果。本人對(duì)自定義View也是一知半解,簡單記錄下自己學(xué)習(xí)自定義View(繼承自View)的過程,方便日后翻閱。
使用技術(shù)
1、繼承View
2、Canvas
3、paint
效果圖:

1.分析組件
自定義view首先我們要分析組件是由幾部分組成,然后在依次順序使用canvas畫出組件,首先可以看出該組件由一個(gè)背景外部圓,一個(gè)圓弧,以及圓弧端點(diǎn)是由兩個(gè)圓組成,內(nèi)部是三個(gè)文字。分析完畢,我們就可以先定義組件屬性了
2.組件屬性
1.在values目錄下新建attrs.xml文件,用來編寫組件屬性
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ProgressView"> <attr name="title" format="string"/> <attr name="num" format="string"/> <attr name="unit" format="string"/> <attr name="titleTextsize" format="dimension"/> <attr name="numTextsize" format="dimension"/> <attr name="unitTextsize" format="dimension"/> <attr name="titleTextColor" format="color"/> <attr name="numTextColor" format="color"/> <attr name="unitTextColor" format="color"/> <attr name="backCircleWidth" format="dimension"/> <attr name="outerCircleWidth" format="dimension"/> <attr name="backCircleColor" format="color"/> <attr name="outerCircleColor" format="color"/> <attr name="endCircleWidth" format="dimension"/> <attr name="edgeDistance" format="dimension"/> <attr name="endCircleColor" format="color"/> <attr name="currentPercent" format="float"/> </declare-styleable> </resources>
2.自定義view繼承View并實(shí)現(xiàn)構(gòu)造方法
public class ProgressView extends View {
/**
* 在java代碼里new的時(shí)候會(huì)用到
* @param context
*/
public ProgressView(Context context) {
super(context);
init(context, null);
}
/**
* 在xml布局文件中使用時(shí)自動(dòng)調(diào)用
* @param context
*/
public ProgressView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context,attrs);
}
/**
* 不會(huì)自動(dòng)調(diào)用,如果有默認(rèn)style時(shí),在第二個(gè)構(gòu)造函數(shù)中調(diào)用
* @param context
* @param attrs
* @param defStyleAttr
*/
public ProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
2.初始化屬性
/**
* 初始化屬性
* @param context
* @param attrs
*/
private void init(Context context,AttributeSet attrs){
this.mContext = context;
if(attrs!=null){
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProgressView);
title = array.getString(R.styleable.ProgressView_title);
num = array.getString(R.styleable.ProgressView_num);
unit = array.getString(R.styleable.ProgressView_unit);
titleTextsize = array.getDimension(R.styleable.ProgressView_titleTextsize,24);
numTextsize = array.getDimension(R.styleable.ProgressView_numTextsize,48);
unitTextsize = array.getDimension(R.styleable.ProgressView_unitTextsize,24);
titleTextColor = array.getColor(R.styleable.ProgressView_titleTextColor, Color.parseColor("#656d78"));
numTextColor = array.getColor(R.styleable.ProgressView_numTextColor, Color.parseColor("#4fc1e9"));
unitTextColor = array.getColor(R.styleable.ProgressView_unitTextColor, Color.parseColor("#4fc1e9"));
backCircleWidth = array.getDimension(R.styleable.ProgressView_backCircleWidth, 12);
outerCircleWidth = array.getDimension(R.styleable.ProgressView_outerCircleWidth, 20);
backCircleColor = array.getColor(R.styleable.ProgressView_backCircleColor, Color.parseColor("#e6e9ed"));
outerCircleColor = array.getColor(R.styleable.ProgressView_outerCircleColor, Color.parseColor("#4fc1e9"));
endCircleWidth = array.getDimension(R.styleable.ProgressView_endCircleWidth,24);
endCircleColor = array.getColor(R.styleable.ProgressView_endCircleColor, Color.parseColor("#4fc1e9"));
edgeDistance = array.getDimension(R.styleable.ProgressView_edgeDistance, 12);
currentPercent = array.getFloat(R.styleable.ProgressView_currentPercent, 0);
if(currentPercent>1||currentPercent<0){
currentPercent = currentPercent>1?1:0;
}
//初始化畫筆
backCirclePaint = new Paint();
backCirclePaint.setAntiAlias(true);
backCirclePaint.setStrokeWidth(backCircleWidth);
backCirclePaint.setColor(backCircleColor);
backCirclePaint.setStyle(Paint.Style.STROKE);
outerCirclePaint = new Paint();
outerCirclePaint.setAntiAlias(true);
outerCirclePaint.setStrokeWidth(outerCircleWidth);
outerCirclePaint.setColor(outerCircleColor);
outerCirclePaint.setStyle(Paint.Style.STROKE);
endBigCirclePaint = new Paint();
endBigCirclePaint.setAntiAlias(true);
endBigCirclePaint.setStrokeWidth(endCircleWidth);
endBigCirclePaint.setColor(endCircleColor);
endBigCirclePaint.setStyle(Paint.Style.STROKE);
endSmallCirclePaint = new Paint();
endSmallCirclePaint.setAntiAlias(true);
endSmallCirclePaint.setColor(Color.WHITE);
endSmallCirclePaint.setStyle(Paint.Style.FILL);
titlePaint = new Paint();
//通過設(shè)置Flag來應(yīng)用抗鋸齒效果
titlePaint.setFlags(Paint.ANTI_ALIAS_FLAG);
titlePaint.setAntiAlias(true);
//設(shè)置文字居中
//titlePaint.setTextAlign(Paint.Align.CENTER);
titlePaint.setColor(titleTextColor);
titlePaint.setTextSize(titleTextsize);
numPaint = new Paint();
numPaint.setAntiAlias(true);
numPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
//設(shè)置文字居中
//numPaint.setTextAlign(Paint.Align.CENTER);
numPaint.setColor(numTextColor);
numPaint.setTextSize(numTextsize);
unitPaint = new Paint();
unitPaint.setAntiAlias(true);
unitPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
//unitPaint.setTextAlign(Paint.Align.CENTER);
unitPaint.setColor(unitTextColor);
unitPaint.setTextSize(unitTextsize);
//釋放
array.recycle();
}
}
3.獲取組件高度寬度,重寫onMeasure方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
///獲取總寬度,是包含padding值
//處理WAP_CONTENT
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
width = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
height = MeasureSpec.getSize(heightMeasureSpec);
if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
//默認(rèn)大小 200*200
setMeasuredDimension(200,200);
}else if (widthSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(height, height);
}else if (heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(width, width);
}
}
4.重寫onDraw()繪制組件各部分
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//圓心
int centerX = width / 2;
int centerY = height / 2;
//計(jì)算半徑
float radius = centerX - edgeDistance;
//畫背景圓
drawBackCircle(canvas,centerX,centerY,radius);
//繪制圓弧進(jìn)度
drawProgress(canvas,centerX,centerY);
//繪制標(biāo)題
drawText(canvas);
}```
###### 4.1繪制背景圓
```java
/**
* 繪制背景圓
* @param canvas
* @param x 圓心位置x
* @param y 圓心位置y
* @param radius 半徑
*/
private void drawBackCircle(Canvas canvas,int x,int y,float radius){
canvas.drawCircle(x,y,radius,backCirclePaint);
}
4.2 繪制圓弧進(jìn)度
1.注意:圓弧上端點(diǎn)進(jìn)度為0或者100不顯示,此外端點(diǎn)的位置使用sin和cos來確定坐標(biāo);

/**
* 繪制圓弧進(jìn)度
*/
private void drawProgress(Canvas canvas,int x,int y){
//圓弧的范圍
RectF rectF = new RectF(edgeDistance, edgeDistance, width - edgeDistance, height - edgeDistance);
//定義的圓弧的形狀和大小的范圍
// 置圓弧是從哪個(gè)角度來順時(shí)針繪畫的
//設(shè)置圓弧掃過的角度
//設(shè)置我們的圓弧在繪畫的時(shí)候,是否經(jīng)過圓形 這里不需要
//畫筆
canvas.drawArc(rectF, -90, 360 * currentPercent, false, outerCirclePaint);
//繪制端圓
//進(jìn)度在0~100%的時(shí)候才會(huì)畫終點(diǎn)小圓,可以自由改動(dòng)
if(currentPercent>0&¤tPercent<1){
//繪制外層大圓
canvas.drawCircle(x + rectF.width() / 2 * (float) Math.sin(360 * currentPercent * Math.PI / 180),
y - rectF.width() / 2 * (float) Math.cos(360 * currentPercent * Math.PI / 180), endCircleWidth / 2, endBigCirclePaint);
//繪制內(nèi)層圓點(diǎn)
canvas.drawCircle(x + rectF.width() / 2 * (float) Math.sin(360 * currentPercent * Math.PI / 180),
y - rectF.width() / 2 * (float) Math.cos(360 * currentPercent * Math.PI / 180), endCircleWidth / 4, endSmallCirclePaint);
}
}
4.3 繪制文字
/**
* 繪制標(biāo)題
* @param canvas
*/
private void drawText(Canvas canvas) {
Rect textRect = new Rect();
//返回的則是當(dāng)前文本所需要的最小寬度,也就是整個(gè)文本外切矩形的寬度
titlePaint.getTextBounds(title, 0, title.length(), textRect);//25 50 175
//高度平分四部分
float h = height/ 4;
//文字居中
canvas.drawText(title, width / 2 - textRect.width() / 2, h + textRect.height() / 2, titlePaint);
numPaint.getTextBounds(num, 0, num.length(), textRect);
canvas.drawText(num, width / 2 - textRect.width() / 2, h*2 + textRect.height() / 2, numPaint);
unitPaint.getTextBounds(unit, 0, unit.length(), textRect);
canvas.drawText(unit, width / 2 - textRect.width() / 2, 3*h + textRect.height() / 2, unitPaint);
}
4.4提供外部修改進(jìn)度方法以及進(jìn)度過度
/**
* 設(shè)置進(jìn)度
*/
public void setProgress(final float progress){
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<=progress*100;i++){
Message msg = new Message();
msg.what = 1;
msg.obj = i;
try {
Thread.sleep(20);
handler.sendMessage(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
private Handler handler = new Handler(new Handler.Callback(){
@Override
public boolean handleMessage(@NonNull Message msg) {
if(msg.what==1){
currentPercent = ((float)Integer.valueOf(msg.obj+"")/100);
System.out.println("更新"+currentPercent);
invalidate();
}
return false;
}
});
總結(jié):使用Canvas的drawArc方法繪制圓弧及drawText繪制文本信息等;
github地址:項(xiàng)目地址
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android OpenGL ES 實(shí)現(xiàn)抖音傳送帶特效(原理解析)
這篇文章主要介紹了Android OpenGL ES 實(shí)現(xiàn)抖音傳送帶特效,抖音傳送帶特效推出已經(jīng)很長一段時(shí)間了,前面也實(shí)現(xiàn)了下,最近把它整理出來了,如果你有仔細(xì)觀測傳送帶特效,就會(huì)發(fā)現(xiàn)它的實(shí)現(xiàn)原理其實(shí)很簡單,需要的朋友可以參考下2022-07-07
android效果TapBarMenu繪制底部導(dǎo)航欄的使用方式示例
本篇文章主要介紹了android效果TapBarMenu繪制底部導(dǎo)航欄的使用方式,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01
使用Android Studio實(shí)現(xiàn)為系統(tǒng)級(jí)的app簽名
這篇文章主要介紹了使用Android Studio實(shí)現(xiàn)為系統(tǒng)級(jí)的app簽名,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-03-03
詳解Android studio ndk配置cmake開發(fā)native C
這篇文章主要介紹了詳解Android studio ndk配置cmake開發(fā)native C,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-09-09
Android 中對(duì)JSON數(shù)據(jù)解析實(shí)例代碼
這篇文章主要介紹了Android 中對(duì)JSON數(shù)據(jù)解析實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-03-03
Android中SurfaceView和view畫出觸摸軌跡
這篇文章主要介紹了Android中SurfaceView和view畫出觸摸軌跡的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-03-03
Android小知識(shí)之圖片的3種壓縮方式小結(jié)
這篇文章主要給大家介紹了關(guān)于Android小知識(shí)之圖片的3種壓縮方式的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-10-10
Android itemDecoration接口實(shí)現(xiàn)吸頂懸浮標(biāo)題
這篇文章主要介紹了Android中使用itemdecoration實(shí)現(xiàn)吸頂懸浮標(biāo)題,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-11-11
Android中button點(diǎn)擊后字體的變色效果
button的點(diǎn)擊效果無疑是非常簡單的,接下來通過本文給大家介紹下如何添加button點(diǎn)擊的字體顏色變化效果,感興趣的朋友一起看看吧2016-10-10

