好幾天不寫博客了余寥,這段時間一直沒時間,感覺一直在忙悯森,但是進度不大宋舷。
好了,言歸正傳瓢姻,最近項目里要用到這么一個自定義view祝蝠,是一個圓形的進度圓環(huán),現(xiàn)在學(xué)習(xí)下怎么來自定義它幻碱。
image.png
源碼下載地址
https://github.com/baojie0327/ViewAndGroup
自定義之前先分析一下续膳,這個自定義View主要有以下幾個部分組成:
- 最外層的圓環(huán)
- 圓環(huán)上的小圓點,會隨著進度移動
- 圓弧收班,會隨著小圓點移動,圓環(huán)上有個漸變度
- 文字
- 添加動畫
1. 第一步還是先考慮一下進度圓環(huán)的屬性信息谒兄。
我這里可以設(shè)置的有摔桦,背景顏色,最外層圓環(huán)的寬度,最外層圓環(huán)的顏色邻耕,進度圓環(huán)的顏色(只不過這里設(shè)置了一個漸進度)鸥咖,圓環(huán)上小圓點的顏色,大小兄世,還有一些文字的設(shè)置啼辣,距離的設(shè)置等。
<!--CircleProgress,血壓測量進度動畫-->
<declare-styleable name="CircleProgress">
<!--背景-->
<attr name="backGroundColor" format="color"></attr>
<!--圓環(huán)的顏色-->
<attr name="ringColor" format="color"></attr>
<!--圓環(huán)的寬度-->
<attr name="ringSize" format="dimension"></attr>
<!--進度圓環(huán)的顏色-->
<attr name="ringprogressColor" format="color"></attr>
<!--圓環(huán)上的小圓點顏色-->
<attr name="dotColor" format="color"></attr>
<!--圓環(huán)上的小圓點大小-->
<attr name="dotSize" format="dimension"></attr>
<!--進度字體顏色-->
<attr name="textProgressColor" format="color"></attr>
<!--進度字體大小-->
<attr name="textProgressSize" format="dimension"></attr>
<!--百分號顏色-->
<attr name="textPercentColor" format="color"></attr>
<!--固定字體設(shè)置-->
<attr name="showProgressText" format="string"></attr>
<!--固定字體顏色-->
<attr name="texColor" format="color"></attr>
<!--固定字體大小-->
<attr name="texSize" format="dimension"></attr>
<!--文字之間的距離-->
<attr name="texMarginSize" format="dimension"></attr>
<!--固定字體大小-->
<attr name="setNumber" format="integer"></attr>
</declare-styleable>
2. 當(dāng)然御滩,我們自定義view的名字必須叫CircleProgress鸥拧,和上面attrs.xml里的名字相同,你可以試試如果不同會出現(xiàn)什么后果削解。
獲得我們在attrs.xml中定義的屬性
public CircleProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//獲得atts.xml定義的屬性值富弦,存儲在TypedArray中
TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircleProgress, defStyleAttr, 0);
int n = ta.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = ta.getIndex(i);
switch (attr) {
case R.styleable.CircleProgress_ringColors: //圓環(huán)顏色
ring_color = ta.getColor(attr, Color.BLACK);
break;
case R.styleable.CircleProgress_ringSize: //圓環(huán)寬度
ringSize = ta.getDimension(R.styleable.CircleProgress_ringSize, 13);
break;
case R.styleable.CircleProgress_ringprogressColor: //圓環(huán)進度顏色
ring_progress_color = ta.getColor(attr, Color.WHITE);
break;
case R.styleable.CircleProgress_dotColor: //小圓點
dot_color = ta.getColor(attr, Color.WHITE);
break;
case R.styleable.CircleProgress_dotSize: //小圓點大小
dotSize = ta.getDimension(R.styleable.CircleProgress_dotSize, 32);
break;
case R.styleable.CircleProgress_textProgressColor: //字體進度的顏色
text_progress_color = ta.getColor(attr, Color.BLACK);
break;
case R.styleable.CircleProgress_textProgressSize: //字體進度大小
textProgressSize = ta.getDimension(R.styleable.CircleProgress_textProgressSize, 32);
break;
case R.styleable.CircleProgress_textPercentColor: //百分號顏色
percent_color = ta.getColor(attr, Color.BLACK);
break;
case R.styleable.CircleProgress_showProgressText: //固定字體顯示
showText = ta.getString(R.styleable.CircleProgress_showProgressText);
break;
case R.styleable.CircleProgress_texColor: //字體的顏色
text_color = ta.getColor(attr, Color.BLACK);
break;
case R.styleable.CircleProgress_texSize: //字體大小
texSize = ta.getDimension(R.styleable.CircleProgress_texSize, 17);
break;
case R.styleable.CircleProgress_texMarginSize: //字之間的款阿杜
texMarginSize = ta.getDimension(R.styleable.CircleProgress_texMarginSize, 9);
break;
}
}
ta.recycle();
init();
}
重寫onMeasure()方法,讓控件支持wrap_content屬性
/**
* 重寫onMeasure()方法氛驮,支持wrap_content屬性
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width;
int height;
int widthMode = MeasureSpec.getMode(widthMeasureSpec); //寬度的測量模式
int widthSize = MeasureSpec.getSize(widthMeasureSpec); //寬度的測量值
int heightMode = MeasureSpec.getMode(heightMeasureSpec); //高度的測量模式
int heightSize = MeasureSpec.getSize(heightMeasureSpec); //高度的測量值
//如果布局里面設(shè)置的是固定值,這里取布局里面的固定值;如果設(shè)置的是match_parent,則取父布局的大小
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
//如果布局里面沒有設(shè)置固定值,這里取布局的寬度的1/2
width = widthSize * 1 / 2;
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
//如果布局里面沒有設(shè)置固定值,這里取布局的高度的3/4
height = heightSize * 3 / 4;
}
setMeasuredDimension(width, height);
}
畫最外層的圓
// 畫最外層的大圓環(huán)
int centre = getWidth() / 2; //獲取圓心的x坐標
int radius = (int) (centre - 2 * ringSize); //圓環(huán)的半徑
mPaint.setColor(ring_color); //設(shè)置圓環(huán)的顏色
mPaint.setStyle(Paint.Style.STROKE); //設(shè)置空心
mPaint.setStrokeWidth(ringSize); //設(shè)置圓環(huán)的寬度
mPaint.setAntiAlias(true); //消除鋸齒
canvas.drawCircle(centre, centre, radius, mPaint); //畫出圓環(huán)
畫文字
//畫進度文字
//設(shè)置進度值的大小顏色腕柜,字體樣式
textPaint.setStrokeWidth(0);
textPaint.setColor(text_progress_color);
textPaint.setTextSize(textProgressSize);
textPaint.setTypeface(Typeface.MONOSPACE);
//中間的進度百分比,先轉(zhuǎn)換成float在進行除法運算矫废,不然都為0
int percent = (int) (((float) tempProgress / (float) maxProgress) * 100);
String percent_draw;
if (percent == 0) {
percent_draw = "00";
} else {
percent_draw = percent + "";
}
float textHeight = textProgressSize; //進度字體的高度
float textWidth = textPaint.measureText(percent_draw);
textPaint.setTextSize(textHeight);
// canvas.drawText(percent_draw, centre - 5 * textWidth, centre, textPaint);
canvas.drawText(percent_draw, centre - textWidth / 2, centre + textProgressSize / 6, textPaint);
//畫百分比號
textPaint.setStrokeWidth(3);
String text_percent = "%";
textPaint.setTextSize(textHeight / 3);
// canvas.drawText(text_percent, centre + 5 * textWidth , centre + textWidth / 2, textPaint);
canvas.drawText("%", centre + textWidth / 2, centre + textProgressSize / 8, textPaint);
//畫展示文字
textPaint.setColor(text_color);
textPaint.setTextSize(texSize);
canvas.drawText(showText, centre - textProgressSize / 2 - 10, centre + textProgressSize / 3 + texMarginSize, textPaint);
畫圓弧
在畫圓弧的過程中盏缤,我們創(chuàng)建了一個SweepGradient渲染器,來畫我們的漸進色圓環(huán)蓖扑。直接通過 ringProgressPaint.setShader(mSweepGradient)設(shè)置給Paint即可
//畫圓弧
ringProgressPaint.setStyle(Paint.Style.STROKE);
ringProgressPaint.setStrokeWidth(ringSize);
// ringProgressPaint.setColor(ring_progress_color);
RectF oval = new RectF(centre - radius, centre - radius, centre
+ radius, centre + radius); //用于定義的圓弧的形狀和大小的界限
//創(chuàng)建一個渲染器
SweepGradient mSweepGradient=new SweepGradient(canvas.getWidth()/2
,canvas.getHeight()/2,new int[]{Color.rgb(130,213,131),Color.rgb(150,251,196),Color.rgb(130,213,131)},null);
Matrix matrix=new Matrix();
matrix.setRotate(-90f,canvas.getWidth()/2,canvas.getHeight()/2);
mSweepGradient.setLocalMatrix(matrix);
ringProgressPaint.setShader(mSweepGradient);
canvas.drawArc(oval, 90, 360 * tempProgress / maxProgress, false, ringProgressPaint);
畫小圓點
其中有個計算來計算小圓點的坐標唉铜,你可以修改這個計算部分來設(shè)置小圓點的位置,我把它放在了最下面的位置赵誓。
//畫圓點
// 畫進度點 30°角度 的弧度 = 2 * PI / 360 * 30
int rangle = 0;
if (tempProgress == 0) {
rangle = 360 / maxProgress;
} else {
rangle = 360 * (int) tempProgress / maxProgress;
}
double a = 0.0;//角度
int pointX = 0;
int pointY = 0;
if (rangle > 0 && rangle <= 90) {
a = 2 * Math.PI / 360 * (270 - rangle);
pointX = centre + (int) (radius * Math.cos(a));
pointY = centre - (int) (radius * Math.sin(a));
} else if (rangle > 90 && rangle <= 180) {
a = 2 * Math.PI / 360 * (rangle + 90);
pointX = centre + (int) (radius * Math.cos(a));
pointY = centre + (int) (radius * Math.sin(a));
} else if (rangle > 180 && rangle <= 270) {
a = 2 * Math.PI / 360 * (rangle);
pointX = centre - (int) (radius * Math.sin(a));
pointY = centre + (int) (radius * Math.cos(a));
} else if (rangle > 270 && rangle <= 360) {
a = 2 * Math.PI / 360 * (rangle - 90);
pointX = centre - (int) (radius * Math.cos(a));
pointY = centre - (int) (radius * Math.sin(a));
}
pointPaint.setColor(dot_color);
pointPaint.setStyle(Paint.Style.FILL);
pointPaint.setAntiAlias(true); //消除鋸齒
pointPaint.setShadowLayer(10, 0, 0, Color.GRAY);
// Log.d("TAG", "pointX = " + pointX + "||pointY = " + pointY);
canvas.drawCircle(pointX, pointY, dotSize, pointPaint);
其他的方法
主要是設(shè)置圓環(huán)的進度和動畫的一些方法
public synchronized void setMax(int maxProgress) {
if (maxProgress < 0) {
throw new IllegalArgumentException("maxProgress not less than 0");
}
this.maxProgress = maxProgress;
}
/**
* 開啟動畫
*
* @param curProgress
*/
public void setProgressWithAnimation(int curProgress) {
this.curProgress = curProgress;
animator = ValueAnimator.ofInt(0, curProgress);
animator.setDuration(30000);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
tempProgress = (int) animation.getAnimatedValue();
invalidate();
}
});
animator.start();
}
/**
* 完成測量
*
* @param curProgress
*/
public void completeMeasure(int curProgress) {
animator.cancel();
this.curProgress = curProgress;
tempProgress = curProgress;
invalidate();
}
/**
* 停止Progress
*/
public void stopProgress(){
animator.cancel();
invalidate();
}
這里自定義的CircleProgress就定義完了打毛,接下來可以在xml文件中引用,然后通過下面兩行代碼開啟
mCircleProgress.setMax(100);
mCircleProgress.setProgressWithAnimation(100);
當(dāng)然你也可以根據(jù)自己的需求添加其他的方法俩功,再次就不一一添加了幻枉。
直接運行看效果圖吧
Animation1.gif