此次新項目來了一個不一樣尋常進度條,效果圖以下,廢話很少說,直接從進度條背景、虛線圓弧、漸變背景圓弧、進度數值顯示、文字介紹、進度條當前點android
本篇主要涉及到的方法以下:drawArc、drawCircle、drawText,涉及的類Rect、RectFcanvas
相關變量的設置,能夠根據本身的需求給這些變量加set\get方法以及style到xml對應的屬性設置,這裏不實現數組
//圓弧開始的角度
private float startAngle = 170;
//圓弧結束的角度
private float endAngle = 45;
//圓弧背景的開始和結束間的夾角大小
private float mAngle = 200;
//當前進度夾角大小
private float mIncludedAngle = 0;
複製代碼
建立Paint設置對應的屬性bash
mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//抗鋸齒
mArcPaint.setAntiAlias(true);
//圓弧的背景色
mArcPaint.setColor(Color.parseColor("#14FFFFFF"));
//設置透明度(數值爲0-255)
// mArcPaint.setAlpha(100);
//設置畫筆的畫出的形狀
mArcPaint.setStrokeJoin(Paint.Join.ROUND);
mArcPaint.setStrokeCap(Paint.Cap.ROUND);
//設置畫筆類型
mArcPaint.setStyle(Paint.Style.STROKE);
mArcPaint.setStrokeWidth(dp2px(mStrokeWith));
複製代碼
繪製默認的圓弧的背景ide
//繪製圓弧背景
RectF mRectF = new RectF(mStrokeWith + dp2px(5), mStrokeWith + dp2px(5), getWidth() - mStrokeWith - dp2px(5), getHeight() - mStrokeWith);
canvas.drawArc(mRectF, startAngle, mAngle, false, mArcPaint);
複製代碼
繪製進度圓弧函數
//根據當前數據繪製對應的圓弧
canvas.drawArc(mRectF, startAngle, mIncludedAngle, false, mArcPaint);
複製代碼
虛線圓弧與實現的圓弧的方法是同樣的,主要是設置虛線方法setPathEffect,咱們這裏用DashPathEffect,構造函數DashPathEffect(float intervals[], float phase),intervals:控制實線和實線以後空白線的寬度(數組長度必須爲偶數)phase: 將View向」左「偏移phase佈局
代碼實現post
//繪製圓弧背景
mArcPaint.setStrokeWidth(dp2px(mPointStrokeWith));
//設置虛線
mArcPaint.setPathEffect(new DashPathEffect(new float[]{dp2px(1), dp2px(6)}, 0));
RectF mRectF = new RectF(mPointStrokeWith + dp2px(20), mPointStrokeWith + dp2px(20), getWidth() - mPointStrokeWith - dp2px(20), getHeight() - mPointStrokeWith - dp2px(20));
canvas.drawArc(mRectF, startAngle, mAngle, false, mArcPaint);
複製代碼
這裏的漸變咱們用的是LinearGradient線性漸變,Paint的初始化見最後的所有代碼優化
而後給Paint設置漸變背景動畫
linearGradient = new LinearGradient(0, dp2px(40), 0, height / 2, changeColors, null, Shader.TileMode.CLAMP);
mBkgArcPaint.setShader(linearGradient);
複製代碼
最終用設置好的畫筆去繪製漸變背景
//繪製圓弧背景
RectF mRectF = new RectF(mPointStrokeWith + dp2px(33), mPointStrokeWith + dp2px(33), getWidth() - mPointStrokeWith - dp2px(33), getHeight() - mPointStrokeWith - dp2px(33));
canvas.drawArc(mRectF, startAngle, mAngle, false, mBkgArcPaint);
複製代碼
進度數值、文字介紹就要用到drawText去繪製文本了,mTextPaint也是設置畫筆對應的屬性值,而後開始畫
Rect mRect = new Rect();
String mValue = String.valueOf(mAnimatorValue);
//繪製中心的數值
mTextPaint.setTextSize(dp2px(40));
mTextPaint.getTextBounds(mValue, 0, mValue.length(), mRect);
canvas.drawText(String.valueOf(mAnimatorValue), centerX, centerY - 0.2f * mRect.height(), mTextPaint);
//繪製中心數值的百分比
mTextPaint.setTextSize(dp2px(20));
mTextPaint.getTextBounds(mValue, 0, mValue.length(), mRect);
//x軸:centerX+繪製中心的數值的寬度/2
canvas.drawText(mChar, centerX + width / 5, centerY - height / 30, mTextPaint);
// canvas.drawText(mChar, centerX + mRect.width() / 2.0f + 1.6f * mRect.height(), centerY - 0.8f * mRect.height(), mTextPaint);
//繪製中心文字描述
mTextPaint.setTextSize(dp2px(14));
mTextPaint.getTextBounds(mDes, 0, mDes.length(), mRect);
canvas.drawText(mDes, centerX, centerY + 1.6f * mRect.height(), mTextPaint);
複製代碼
繪製進度條當前點,其實就是大圓弧所在點的座標,以正東面爲0度起點計算指定角度所對應的圓周上的點的座標:要用到的方法以下:
float radian = Math.toRadians(angle);
float x = center.x + Math.cos(radian)*currentAdius;
float y = center.y + Math.sin(radian)*currentAdius;
複製代碼
說明angle爲弧度,center.x爲大圓弧圓心所在的X座標,center.y爲大圓弧圓心所在Y座標,currentAdius爲圓的半徑
獲得x,y座標繪製圓就畫出了進度條當前點,因爲效果圖是外層白色裏面一個紅色,則須要畫兩個圓
//根據當前數據繪製對應的夫圓
float radian = (float) Math.toRadians(startAngle + mIncludedAngle);
float x = (float) (centerX + (pRadius - radius) * Math.cos(radian));
float y = (float) (centerY + (pRadius - radius) * Math.sin(radian));
canvas.drawCircle(x, y, radius, mPCirclePaint);
//根據當前數據繪製對應的子圓
canvas.drawCircle(x, y, radius / 2, mCCirclePaint);
複製代碼
至此效果圖的效果就作出來了,再給效果圖的進度變化及其數值變化加個動畫
動態設置的方法
/**
* 設置數據
*
* @param minValue 最小值
* @param maxValue 最大值
* @param currentValue 當前繪製的值
* @param des 描述信息
*/
public void setValues(int minValue, int maxValue, int currentValue, String des) {
mDes = des;
mMaxValue = maxValue;
mMinValue = minValue;
//徹底覆蓋
if (currentValue > maxValue) {
currentValue = maxValue;
}
//計算弧度比重
float scale = (float) currentValue / maxValue;
//計算弧度
float currentAngle = scale * mAngle;
//開始執行動畫
setAnimation(0, currentAngle, currentValue, time);
}
複製代碼
動畫的方法
/**
* 爲繪製弧度及數據設置動畫
*
* @param startAngle 開始的弧度
* @param currentAngle 須要繪製的弧度
* @param currentValue 須要繪製的數據
* @param time 動畫執行的時長
*/
private void setAnimation(float startAngle, float currentAngle, int currentValue, int time) {
//繪製當前數據對應的圓弧的動畫效果
ValueAnimator progressAnimator = ValueAnimator.ofFloat(startAngle, currentAngle);
progressAnimator.setDuration(time);
progressAnimator.setTarget(mIncludedAngle);
progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mIncludedAngle = (float) animation.getAnimatedValue();
//從新繪製,否則不會出現效果
postInvalidate();
}
});
//開始執行動畫
progressAnimator.start();
//中心數據的動畫效果
ValueAnimator valueAnimator = ValueAnimator.ofInt(mAnimatorValue, currentValue);
valueAnimator.setDuration(time);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mAnimatorValue = (int) valueAnimator.getAnimatedValue();
postInvalidate();
}
});
valueAnimator.start();
}
複製代碼
至此效果圖就動起來了,我複用了mArcPaint屢次,能夠根據本身的習慣多建立幾個Paint,總體寫完了代碼奉上,你們可本身再優化一下
自定義進度條完整代碼
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.animation.LinearInterpolator;
/**
* @author madreain
* @date 2019-07-12.
* module:
* description:
*/
public class ArcView extends View {
//根據數據顯示的圓弧Paint
private Paint mArcPaint;
//背景圓
private Paint mBkgArcPaint;
//圓點
private Paint mPCirclePaint;
private Paint mCCirclePaint;
private float radius = dp2px(6);
//漸變處理
private LinearGradient linearGradient;
//漸變的顏色
private int[] changeColors = new int[]{Color.parseColor("#FFFFFF"), Color.parseColor("#00FFFFFF")};
//文字描述的paint
private Paint mTextPaint;
//圓弧開始的角度
private float startAngle = 170;
//圓弧結束的角度
private float endAngle = 45;
//圓弧背景的開始和結束間的夾角大小
private float mAngle = 200;
//當前進度夾角大小
private float mIncludedAngle = 0;
//圓弧的畫筆的寬度
private float mStrokeWith = 4;
//斷點圓弧的畫筆的寬度
private float mPointStrokeWith = 1;
//中心的文字描述
private String mDes = "";
//中心的文字描述邊上的百分號
private String mChar = "%";
//動畫效果的數據及最大/小值
private int mAnimatorValue, mMinValue, mMaxValue;
//中心點的XY座標
private float centerX, centerY;
//最外層大圓的半徑
private float pRadius;
private float height, width;
//動畫時間
private int time = 10000;
public ArcView(Context context) {
this(context, null);
//初始化paint
initPaint();
}
public ArcView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
//初始化paint
initPaint();
}
public ArcView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//初始化paint
initPaint();
}
private void initPaint() {
//圓弧的paint
mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//抗鋸齒
mArcPaint.setAntiAlias(true);
//圓弧的背景色
mArcPaint.setColor(Color.parseColor("#14FFFFFF"));
//設置透明度(數值爲0-255)
// mArcPaint.setAlpha(100);
//設置畫筆的畫出的形狀
mArcPaint.setStrokeJoin(Paint.Join.ROUND);
mArcPaint.setStrokeCap(Paint.Cap.ROUND);
//設置畫筆類型
mArcPaint.setStyle(Paint.Style.STROKE);
mArcPaint.setStrokeWidth(dp2px(mStrokeWith));
//夫圓點
mPCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//抗鋸齒
mPCirclePaint.setAntiAlias(true);
//圓弧的背景色
mPCirclePaint.setColor(Color.parseColor("#FFFFFF"));
//設置畫筆的畫出的形狀
mPCirclePaint.setStrokeJoin(Paint.Join.ROUND);
mPCirclePaint.setStrokeCap(Paint.Cap.ROUND);
//設置畫筆類型
mPCirclePaint.setStyle(Paint.Style.FILL);
//子圓點
mCCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//抗鋸齒
mCCirclePaint.setAntiAlias(true);
//圓弧的背景色
mCCirclePaint.setColor(Color.parseColor("#FF6672"));
//設置畫筆的畫出的形狀
mCCirclePaint.setStrokeJoin(Paint.Join.ROUND);
mCCirclePaint.setStrokeCap(Paint.Cap.ROUND);
//設置畫筆類型
mCCirclePaint.setStyle(Paint.Style.FILL);
//背景填充圓弧的paint
mBkgArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//抗鋸齒
mBkgArcPaint.setAntiAlias(true);
//圓弧的背景色
mBkgArcPaint.setColor(Color.parseColor("#4DFFFFFF"));
//設置畫筆類型
mBkgArcPaint.setStyle(Paint.Style.FILL);
//中心文字的paint
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(Color.parseColor("#FFFFFF"));
//設置文本的對齊方式
mTextPaint.setTextAlign(Paint.Align.CENTER);
//mTextPaint.setTextSize(getResources().getDimensionPixelSize(R.dimen.dp_12));
mTextPaint.setTextSize(dp2px(40));
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
height = h;
width = w;
pRadius = w / 2;
centerX = w / 2;
centerY = h / 2;
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//繪製弧度
drawArc(canvas);
//繪製斷點弧度
drawPointArc(canvas);
//繪製背景圓
drawBkgArc(canvas);
//繪製文本
drawText(canvas);
}
/**
* 繪製背景圓
*
* @param canvas
*/
private void drawBkgArc(Canvas canvas) {
//繪製圓弧背景
//漸變背景
linearGradient = new LinearGradient(0, dp2px(40), 0, height / 2, changeColors, null, Shader.TileMode.CLAMP);
mBkgArcPaint.setShader(linearGradient);
RectF mRectF = new RectF(mPointStrokeWith + dp2px(33), mPointStrokeWith + dp2px(33), getWidth() - mPointStrokeWith - dp2px(33), getHeight() - mPointStrokeWith - dp2px(33));
canvas.drawArc(mRectF, startAngle, mAngle, false, mBkgArcPaint);
}
/**
* 繪製文本
*
* @param canvas
*/
private void drawText(Canvas canvas) {
Rect mRect = new Rect();
String mValue = String.valueOf(mAnimatorValue);
//繪製中心的數值
mTextPaint.setTextSize(dp2px(40));
mTextPaint.getTextBounds(mValue, 0, mValue.length(), mRect);
canvas.drawText(String.valueOf(mAnimatorValue), centerX, centerY - 0.2f * mRect.height(), mTextPaint);
//繪製中心數值的百分比
mTextPaint.setTextSize(dp2px(20));
mTextPaint.getTextBounds(mValue, 0, mValue.length(), mRect);
//x軸:centerX+繪製中心的數值的寬度/2
canvas.drawText(mChar, centerX + width / 5, centerY - height / 30, mTextPaint);
// canvas.drawText(mChar, centerX + mRect.width() / 2.0f + 1.6f * mRect.height(), centerY - 0.8f * mRect.height(), mTextPaint);
//繪製中心文字描述
mTextPaint.setTextSize(dp2px(14));
mTextPaint.getTextBounds(mDes, 0, mDes.length(), mRect);
canvas.drawText(mDes, centerX, centerY + 1.6f * mRect.height(), mTextPaint);
//繪製最小值
// String minValue=String.valueOf(mMinValue);
// String maxValue=String.valueOf(mMaxValue);
// mTextPaint.setTextSize(dp2px(18));
// mTextPaint.getTextBounds(minValue,0,minValue.length(),mRect);
// canvas.drawText(minValue, (float) (centerX-0.6*centerX-dp2px(5)), (float) (centerY+0.75*centerY+mRect.height()+dp2px(5)),mTextPaint);
// //繪製最大指
// mTextPaint.getTextBounds(maxValue,0,maxValue.length(),mRect);
// canvas.drawText(maxValue, (float) (centerX+0.6*centerX+dp2px(5)), (float) (centerY+0.75*centerY+mRect.height()+dp2px(5)),mTextPaint);
}
/**
* 繪製當前的斷點圓弧
*
* @param canvas
*/
private void drawPointArc(Canvas canvas) {
//繪製圓弧背景
mArcPaint.setStrokeWidth(dp2px(mPointStrokeWith));
//設置虛線
mArcPaint.setPathEffect(new DashPathEffect(new float[]{dp2px(1), dp2px(6)}, 0));
RectF mRectF = new RectF(mPointStrokeWith + dp2px(20), mPointStrokeWith + dp2px(20), getWidth() - mPointStrokeWith - dp2px(20), getHeight() - mPointStrokeWith - dp2px(20));
canvas.drawArc(mRectF, startAngle, mAngle, false, mArcPaint);
}
/**
* 繪製當前的圓弧
*
* @param canvas
*/
private void drawArc(Canvas canvas) {
mArcPaint.setStrokeWidth(dp2px(mStrokeWith));
mArcPaint.setPathEffect(null);
//圓弧的背景色
mArcPaint.setColor(Color.parseColor("#14FFFFFF"));
//繪製圓弧背景
RectF mRectF = new RectF(mStrokeWith + dp2px(5), mStrokeWith + dp2px(5), getWidth() - mStrokeWith - dp2px(5), getHeight() - mStrokeWith);
canvas.drawArc(mRectF, startAngle, mAngle, false, mArcPaint);
//繪製當前數值對應的圓弧
mArcPaint.setColor(Color.parseColor("#FFFFFF"));
//根據當前數據繪製對應的圓弧
canvas.drawArc(mRectF, startAngle, mIncludedAngle, false, mArcPaint);
//根據當前數據繪製對應的夫圓
float radian = (float) Math.toRadians(startAngle + mIncludedAngle);
float x = (float) (centerX + (pRadius - radius) * Math.cos(radian));
float y = (float) (centerY + (pRadius - radius) * Math.sin(radian));
canvas.drawCircle(x, y, radius, mPCirclePaint);
//根據當前數據繪製對應的子圓
canvas.drawCircle(x, y, radius / 2, mCCirclePaint);
}
/**
* 爲繪製弧度及數據設置動畫
*
* @param startAngle 開始的弧度
* @param currentAngle 須要繪製的弧度
* @param currentValue 須要繪製的數據
* @param time 動畫執行的時長
*/
private void setAnimation(float startAngle, float currentAngle, int currentValue, int time) {
//繪製當前數據對應的圓弧的動畫效果
ValueAnimator progressAnimator = ValueAnimator.ofFloat(startAngle, currentAngle);
progressAnimator.setDuration(time);
progressAnimator.setTarget(mIncludedAngle);
progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mIncludedAngle = (float) animation.getAnimatedValue();
//從新繪製,否則不會出現效果
postInvalidate();
}
});
//開始執行動畫
progressAnimator.start();
//中心數據的動畫效果
ValueAnimator valueAnimator = ValueAnimator.ofInt(mAnimatorValue, currentValue);
valueAnimator.setDuration(time);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mAnimatorValue = (int) valueAnimator.getAnimatedValue();
postInvalidate();
}
});
valueAnimator.start();
}
/**
* 設置數據
*
* @param minValue 最小值
* @param maxValue 最大值
* @param currentValue 當前繪製的值
* @param des 描述信息
*/
public void setValues(int minValue, int maxValue, int currentValue, String des) {
mDes = des;
mMaxValue = maxValue;
mMinValue = minValue;
//徹底覆蓋
if (currentValue > maxValue) {
currentValue = maxValue;
}
//計算弧度比重
float scale = (float) currentValue / maxValue;
//計算弧度
float currentAngle = scale * mAngle;
//開始執行動畫
setAnimation(0, currentAngle, currentValue, time);
}
public float dp2px(float dp) {
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
return dp * metrics.density;
}
}
複製代碼
xml佈局好,在Activity中使用調用方法,就能看見動畫了
arcview.setValues(0,100,100,"預埋與隱蔽");
複製代碼