先上效果图
import androID.animation.ValueAnimator;import androID.content.Context;import androID.content.res.Resources;import androID.content.res.TypedArray;import androID.graphics.Canvas;import androID.graphics.color;import androID.graphics.Paint;import androID.graphics.Point;import androID.graphics.RectF;import androID.graphics.SweepGradIEnt;import androID.graphics.Typeface;import androID.text.TextPaint;import androID.util.AttributeSet;import androID.util.Log;import androID.vIEw.VIEw;import com.littlejIE.circleprogress.utils.Constant;import com.littlejIE.circleprogress.utils.MiscUtil;/** * 圆形进度条,类似 QQ 健康中运动步数的 UI 控件 * Created by littlejIE on 2017/2/21. */public class CircleProgress extends VIEw { private static final String TAG = CircleProgress.class.getSimplename(); private Context mContext; //默认大小 private int mDefaultSize; //是否开启抗锯齿 private boolean antiAlias; //绘制提示 private TextPaint mHintPaint; private CharSequence mHint; private int mHintcolor; private float mHintSize; private float mHintOffset; //绘制单位 private TextPaint munitPaint; private CharSequence munit; private int munitcolor; private float munitSize; private float munitOffset; //绘制数值 private TextPaint mValuePaint; private float mValue; private float mMaxValue; private float mValueOffset; private int mPrecision; private String mPrecisionFormat; private int mValuecolor; private float mValueSize; //绘制圆弧 private Paint marcPaint; private float marcWIDth; private float mStartAngle, mSweepAngle; private RectF mRectF; //渐变的颜色是360度,如果只显示270,那么则会缺失部分颜色 private SweepGradIEnt mSweepGradIEnt; private int[] mGradIEntcolors = {color.GREEN, color.YELLOW, color.RED}; //当前进度,[0.0f,1.0f] private float mPercent; //动画时间 private long mAnimTime; //属性动画 private ValueAnimator mAnimator; //绘制背景圆弧 private Paint mBgArcPaint; private int mBgArccolor; private float mBgArcWIDth; //圆心坐标,半径 private Point mCenterPoint; private float mRadius; private float mTextOffsetPercentInRadius; public CircleProgress(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } private voID init(Context context, AttributeSet attrs) { mContext = context; mDefaultSize = MiscUtil.diptopx(mContext, Constant.DEFAulT_SIZE); mAnimator = new ValueAnimator(); mRectF = new RectF(); mCenterPoint = new Point(); initAttrs(attrs); initPaint(); setValue(mValue); } private voID initAttrs(AttributeSet attrs) { TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.CircleProgressbar); antiAlias = typedArray.getBoolean(R.styleable.CircleProgressbar_antiAlias, Constant.ANTI_AliAS); mHint = typedArray.getString(R.styleable.CircleProgressbar_hint); mHintcolor = typedArray.getcolor(R.styleable.CircleProgressbar_hintcolor, color.BLACK); mHintSize = typedArray.getDimension(R.styleable.CircleProgressbar_hintSize, Constant.DEFAulT_HINT_SIZE); mValue = typedArray.getfloat(R.styleable.CircleProgressbar_value, Constant.DEFAulT_VALUE); mMaxValue = typedArray.getfloat(R.styleable.CircleProgressbar_maxValue, Constant.DEFAulT_MAX_VALUE); //内容数值精度格式 mPrecision = typedArray.getInt(R.styleable.CircleProgressbar_precision, 0); mPrecisionFormat = MiscUtil.getPrecisionFormat(mPrecision); mValuecolor = typedArray.getcolor(R.styleable.CircleProgressbar_valuecolor, color.BLACK); mValueSize = typedArray.getDimension(R.styleable.CircleProgressbar_valueSize, Constant.DEFAulT_VALUE_SIZE); munit = typedArray.getString(R.styleable.CircleProgressbar_unit); munitcolor = typedArray.getcolor(R.styleable.CircleProgressbar_unitcolor, color.BLACK); munitSize = typedArray.getDimension(R.styleable.CircleProgressbar_unitSize, Constant.DEFAulT_UNIT_SIZE); marcWIDth = typedArray.getDimension(R.styleable.CircleProgressbar_arcWIDth, Constant.DEFAulT_ARC_WIDTH); mStartAngle = typedArray.getfloat(R.styleable.CircleProgressbar_startAngle, Constant.DEFAulT_START_ANGLE); mSweepAngle = typedArray.getfloat(R.styleable.CircleProgressbar_sweepAngle, Constant.DEFAulT_SWEEP_ANGLE); mBgArccolor = typedArray.getcolor(R.styleable.CircleProgressbar_bgArccolor, color.WHITE); mBgArcWIDth = typedArray.getDimension(R.styleable.CircleProgressbar_bgArcWIDth, Constant.DEFAulT_ARC_WIDTH); mTextOffsetPercentInRadius = typedArray.getfloat(R.styleable.CircleProgressbar_textOffsetPercentInRadius, 0.33f); //mPercent = typedArray.getfloat(R.styleable.CircleProgressbar_percent, 0); mAnimTime = typedArray.getInt(R.styleable.CircleProgressbar_animTime, Constant.DEFAulT_ANIM_TIME); int gradIEntArccolors = typedArray.getResourceID(R.styleable.CircleProgressbar_arccolors, 0); if (gradIEntArccolors != 0) { try { int[] gradIEntcolors = getResources().getIntArray(gradIEntArccolors); if (gradIEntcolors.length == 0) {//如果渐变色为数组为0,则尝试以单色读取色值 int color = getResources().getcolor(gradIEntArccolors); mGradIEntcolors = new int[2]; mGradIEntcolors[0] = color; mGradIEntcolors[1] = color; } else if (gradIEntcolors.length == 1) {//如果渐变数组只有一种颜色,默认设为两种相同颜色 mGradIEntcolors = new int[2]; mGradIEntcolors[0] = gradIEntcolors[0]; mGradIEntcolors[1] = gradIEntcolors[0]; } else { mGradIEntcolors = gradIEntcolors; } } catch (Resources.NotFoundException e) { throw new Resources.NotFoundException("the give resource not found."); } } typedArray.recycle(); } private voID initPaint() { mHintPaint = new TextPaint(); // 设置抗锯齿,会消耗较大资源,绘制图形速度会变慢。 mHintPaint.setAntiAlias(antiAlias); // 设置绘制文字大小 mHintPaint.setTextSize(mHintSize); // 设置画笔颜色 mHintPaint.setcolor(mHintcolor); // 从中间向两边绘制,不需要再次计算文字 mHintPaint.setTextAlign(Paint.Align.CENTER); mValuePaint = new TextPaint(); mValuePaint.setAntiAlias(antiAlias); mValuePaint.setTextSize(mValueSize); mValuePaint.setcolor(mValuecolor); // 设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等 mValuePaint.setTypeface(Typeface.DEFAulT_BolD); mValuePaint.setTextAlign(Paint.Align.CENTER); munitPaint = new TextPaint(); munitPaint.setAntiAlias(antiAlias); munitPaint.setTextSize(munitSize); munitPaint.setcolor(munitcolor); munitPaint.setTextAlign(Paint.Align.CENTER); marcPaint = new Paint(); marcPaint.setAntiAlias(antiAlias); // 设置画笔的样式,为FILL,FILL_OR_stroke,或stroke marcPaint.setStyle(Paint.Style.stroke); // 设置画笔粗细 marcPaint.setstrokeWIDth(marcWIDth); // 当画笔样式为stroke或FILL_OR_stroke时,设置笔刷的图形样式,如圆形样式 // Cap.ROUND,或方形样式 Cap.SQUARE marcPaint.setstrokeCap(Paint.Cap.ROUND); mBgArcPaint = new Paint(); mBgArcPaint.setAntiAlias(antiAlias); mBgArcPaint.setcolor(mBgArccolor); mBgArcPaint.setStyle(Paint.Style.stroke); mBgArcPaint.setstrokeWIDth(mBgArcWIDth); mBgArcPaint.setstrokeCap(Paint.Cap.ROUND); } @OverrIDe protected voID onMeasure(int wIDthMeasureSpec, int heightmeasureSpec) { super.onMeasure(wIDthMeasureSpec, heightmeasureSpec); setMeasuredDimension(MiscUtil.measure(wIDthMeasureSpec, mDefaultSize), MiscUtil.measure(heightmeasureSpec, mDefaultSize)); } @OverrIDe protected voID onSizeChanged(int w, int h, int olDW, int oldh) { super.onSizeChanged(w, h, olDW, oldh); Log.d(TAG, "onSizeChanged: w = " + w + "; h = " + h + "; olDW = " + olDW + "; oldh = " + oldh); //求圆弧和背景圆弧的最大宽度 float maxArcWIDth = Math.max(marcWIDth, mBgArcWIDth); //求最小值作为实际值 int minSize = Math.min(w - getpaddingleft() - getpaddingRight() - 2 * (int) maxArcWIDth, h - getpaddingtop() - getpaddingBottom() - 2 * (int) maxArcWIDth); //减去圆弧的宽度,否则会造成部分圆弧绘制在外围 mRadius = minSize / 2; //获取圆的相关参数 mCenterPoint.x = w / 2; mCenterPoint.y = h / 2; //绘制圆弧的边界 mRectF.left = mCenterPoint.x - mRadius - maxArcWIDth / 2; mRectF.top = mCenterPoint.y - mRadius - maxArcWIDth / 2; mRectF.right = mCenterPoint.x + mRadius + maxArcWIDth / 2; mRectF.bottom = mCenterPoint.y + mRadius + maxArcWIDth / 2; //计算文字绘制时的 baseline //由于文字的baseline、descent、ascent等属性只与textSize和typeface有关,所以此时可以直接计算 //若value、hint、unit由同一个画笔绘制或者需要动态设置文字的大小,则需要在每次更新后再次计算 mValueOffset = mCenterPoint.y + getBaselineOffsetFromY(mValuePaint); mHintOffset = mCenterPoint.y - mRadius * mTextOffsetPercentInRadius + getBaselineOffsetFromY(mHintPaint); munitOffset = mCenterPoint.y + mRadius * mTextOffsetPercentInRadius + getBaselineOffsetFromY(munitPaint); updateArcPaint(); Log.d(TAG, "onSizeChanged: 控件大小 = " + "(" + w + ", " + h + ")" + "圆心坐标 = " + mCenterPoint.toString() + ";圆半径 = " + mRadius + ";圆的外接矩形 = " + mRectF.toString()); } private float getBaselineOffsetFromY(Paint paint) { return MiscUtil.measureTextHeight(paint) / 2; } @OverrIDe protected voID onDraw(Canvas canvas) { super.onDraw(canvas); drawText(canvas); drawArc(canvas); } /** * 绘制内容文字 * * @param canvas */ private voID drawText(Canvas canvas) { // 计算文字宽度,由于Paint已设置为居中绘制,故此处不需要重新计算 // float textWIDth = mValuePaint.measureText(mValue.toString()); // float x = mCenterPoint.x - textWIDth / 2; canvas.drawText(String.format(mPrecisionFormat, mValue), mCenterPoint.x, mValueOffset, mValuePaint); if (mHint != null) { canvas.drawText(mHint.toString(), mCenterPoint.x, mHintOffset, mHintPaint); } if (munit != null) { canvas.drawText(munit.toString(), mCenterPoint.x, munitOffset, munitPaint); } } private voID drawArc(Canvas canvas) { // 绘制背景圆弧 // 从进度圆弧结束的地方开始重新绘制,优化性能 canvas.save(); float currentAngle = mSweepAngle * mPercent; canvas.rotate(mStartAngle, mCenterPoint.x, mCenterPoint.y); canvas.drawArc(mRectF, currentAngle, mSweepAngle - currentAngle + 2, false, mBgArcPaint); // 第一个参数 oval 为 RectF 类型,即圆弧显示区域 // startAngle 和 sweepAngle 均为 float 类型,分别表示圆弧起始角度和圆弧度数 // 3点钟方向为0度,顺时针递增 // 如果 startAngle < 0 或者 > 360,则相当于 startAngle % 360 // useCenter:如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形 canvas.drawArc(mRectF, 2, currentAngle, false, marcPaint); canvas.restore(); } /** * 更新圆弧画笔 */ private voID updateArcPaint() { // 设置渐变 mSweepGradIEnt = new SweepGradIEnt(mCenterPoint.x, mCenterPoint.y, mGradIEntcolors, null); marcPaint.setShader(mSweepGradIEnt); } public boolean isAntiAlias() { return antiAlias; } public CharSequence getHint() { return mHint; } public voID setHint(CharSequence hint) { mHint = hint; } public CharSequence getUnit() { return munit; } public voID setUnit(CharSequence unit) { munit = unit; } public float getValue() { return mValue; } /** * 设置当前值 * * @param value */ public voID setValue(float value) { if (value > mMaxValue) { value = mMaxValue; } float start = mPercent; float end = value / mMaxValue; startAnimator(start, end, mAnimTime); } private voID startAnimator(float start, float end, long animTime) { mAnimator = ValueAnimator.offloat(start, end); mAnimator.setDuration(animTime); mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @OverrIDe public voID onAnimationUpdate(ValueAnimator animation) { mPercent = (float) animation.getAnimatedValue(); mValue = mPercent * mMaxValue; if (BuildConfig.DEBUG) { Log.d(TAG, "onAnimationUpdate: percent = " + mPercent + ";currentAngle = " + (mSweepAngle * mPercent) + ";value = " + mValue); } invalIDate(); } }); mAnimator.start(); } /** * 获取最大值 * * @return */ public float getMaxValue() { return mMaxValue; } /** * 设置最大值 * * @param maxValue */ public voID setMaxValue(float maxValue) { mMaxValue = maxValue; } /** * 获取精度 * * @return */ public int getPrecision() { return mPrecision; } public voID setPrecision(int precision) { mPrecision = precision; mPrecisionFormat = MiscUtil.getPrecisionFormat(precision); } public int[] getGradIEntcolors() { return mGradIEntcolors; } /** * 设置渐变 * * @param gradIEntcolors */ public voID setGradIEntcolors(int[] gradIEntcolors) { mGradIEntcolors = gradIEntcolors; updateArcPaint(); } public long getAnimTime() { return mAnimTime; } public voID setAnimTime(long animTime) { mAnimTime = animTime; } /** * 重置 */ public voID reset() { startAnimator(mPercent, 0.0f, 1000L); } @OverrIDe protected voID onDetachedFromWindow() { super.onDetachedFromWindow(); //释放资源 }}
import androID.animation.ValueAnimator;import androID.content.Context;import androID.content.res.Resources;import androID.content.res.TypedArray;import androID.graphics.Canvas;import androID.graphics.color;import androID.graphics.Paint;import androID.graphics.Point;import androID.graphics.RectF;import androID.graphics.SweepGradIEnt;import androID.graphics.Typeface;import androID.text.TextPaint;import androID.util.AttributeSet;import androID.util.Log;import androID.vIEw.VIEw;import com.littlejIE.circleprogress.utils.Constant;import com.littlejIE.circleprogress.utils.MiscUtil;/** * 带有刻度的圆形进度条 * Created by littlejIE on 2017/2/26. */public class DialProgress extends VIEw { private static final String TAG = DialProgress.class.getSimplename(); private Context mContext; //圆心坐标 private Point mCenterPoint; private float mRadius; private float mTextOffsetPercentInRadius; private boolean antiAlias; //绘制提示 private TextPaint mHintPaint; private CharSequence mHint; private int mHintcolor; private float mHintSize; private float mHintOffset; //绘制数值 private Paint mValuePaint; private int mValuecolor; private float mMaxValue; private float mValue; private float mValueSize; private float mValueOffset; private String mPrecisionFormat; //绘制单位 private Paint munitPaint; private float munitSize; private int munitcolor; private float munitOffset; private CharSequence munit; //前景圆弧 private Paint marcPaint; private float marcWIDth; private int mDialintervalDegree; private float mStartAngle, mSweepAngle; private RectF mRectF; //渐变 private int[] mGradIEntcolors = {color.GREEN, color.YELLOW, color.RED}; //当前进度,[0.0f,1.0f] private float mPercent; //动画时间 private long mAnimTime; //属性动画 private ValueAnimator mAnimator; //背景圆弧 private Paint mBgArcPaint; private int mBgArccolor; //刻度线颜色 private Paint mDialPaint; private float mDialWIDth; private int mDialcolor; private int mDefaultSize; public DialProgress(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } private voID init(Context context, AttributeSet attrs) { mContext = context; mDefaultSize = MiscUtil.diptopx(context, Constant.DEFAulT_SIZE); mRectF = new RectF(); mCenterPoint = new Point(); initConfig(context, attrs); initPaint(); setValue(mValue); } private voID initConfig(Context context, AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.DialProgress); antiAlias = typedArray.getBoolean(R.styleable.DialProgress_antiAlias, true); mMaxValue = typedArray.getfloat(R.styleable.DialProgress_maxValue, Constant.DEFAulT_MAX_VALUE); mValue = typedArray.getfloat(R.styleable.DialProgress_value, Constant.DEFAulT_VALUE); mValueSize = typedArray.getDimension(R.styleable.DialProgress_valueSize, Constant.DEFAulT_VALUE_SIZE); mValuecolor = typedArray.getcolor(R.styleable.DialProgress_valuecolor, color.BLACK); mDialintervalDegree = typedArray.getInt(R.styleable.DialProgress_dialintervalDegree, 10); int precision = typedArray.getInt(R.styleable.DialProgress_precision, 0); mPrecisionFormat = MiscUtil.getPrecisionFormat(precision); munit = typedArray.getString(R.styleable.DialProgress_unit); munitcolor = typedArray.getcolor(R.styleable.DialProgress_unitcolor, color.BLACK); munitSize = typedArray.getDimension(R.styleable.DialProgress_unitSize, Constant.DEFAulT_UNIT_SIZE); mHint = typedArray.getString(R.styleable.DialProgress_hint); mHintcolor = typedArray.getcolor(R.styleable.DialProgress_hintcolor, color.BLACK); mHintSize = typedArray.getDimension(R.styleable.DialProgress_hintSize, Constant.DEFAulT_HINT_SIZE); marcWIDth = typedArray.getDimension(R.styleable.DialProgress_arcWIDth, Constant.DEFAulT_ARC_WIDTH); mStartAngle = typedArray.getfloat(R.styleable.DialProgress_startAngle, Constant.DEFAulT_START_ANGLE); mSweepAngle = typedArray.getfloat(R.styleable.DialProgress_sweepAngle, Constant.DEFAulT_SWEEP_ANGLE); mAnimTime = typedArray.getInt(R.styleable.DialProgress_animTime, Constant.DEFAulT_ANIM_TIME); mBgArccolor = typedArray.getcolor(R.styleable.DialProgress_bgArccolor, color.GRAY); mDialWIDth = typedArray.getDimension(R.styleable.DialProgress_dialWIDth, 2); mDialcolor = typedArray.getcolor(R.styleable.DialProgress_dialcolor, color.WHITE); mTextOffsetPercentInRadius = typedArray.getfloat(R.styleable.DialProgress_textOffsetPercentInRadius, 0.33f); int gradIEntArccolors = typedArray.getResourceID(R.styleable.DialProgress_arccolors, 0); if (gradIEntArccolors != 0) { try { int[] gradIEntcolors = getResources().getIntArray(gradIEntArccolors); if (gradIEntcolors.length == 0) { int color = getResources().getcolor(gradIEntArccolors); mGradIEntcolors = new int[2]; mGradIEntcolors[0] = color; mGradIEntcolors[1] = color; } else if (gradIEntcolors.length == 1) { mGradIEntcolors = new int[2]; mGradIEntcolors[0] = gradIEntcolors[0]; mGradIEntcolors[1] = gradIEntcolors[0]; } else { mGradIEntcolors = gradIEntcolors; } } catch (Resources.NotFoundException e) { throw new Resources.NotFoundException("the give resource not found."); } } typedArray.recycle(); } private voID initPaint() { mHintPaint = new TextPaint(); // 设置抗锯齿,会消耗较大资源,绘制图形速度会变慢。 mHintPaint.setAntiAlias(antiAlias); // 设置绘制文字大小 mHintPaint.setTextSize(mHintSize); // 设置画笔颜色 mHintPaint.setcolor(mHintcolor); // 从中间向两边绘制,不需要再次计算文字 mHintPaint.setTextAlign(Paint.Align.CENTER); mValuePaint = new Paint(); mValuePaint.setAntiAlias(antiAlias); mValuePaint.setTextSize(mValueSize); mValuePaint.setcolor(mValuecolor); mValuePaint.setTypeface(Typeface.DEFAulT_BolD); mValuePaint.setTextAlign(Paint.Align.CENTER); munitPaint = new Paint(); munitPaint.setAntiAlias(antiAlias); munitPaint.setTextSize(munitSize); munitPaint.setcolor(munitcolor); munitPaint.setTextAlign(Paint.Align.CENTER); marcPaint = new Paint(); marcPaint.setAntiAlias(antiAlias); marcPaint.setStyle(Paint.Style.stroke); marcPaint.setstrokeWIDth(marcWIDth); marcPaint.setstrokeCap(Paint.Cap.BUTT); mBgArcPaint = new Paint(); mBgArcPaint.setAntiAlias(antiAlias); mBgArcPaint.setStyle(Paint.Style.stroke); mBgArcPaint.setstrokeWIDth(marcWIDth); mBgArcPaint.setstrokeCap(Paint.Cap.BUTT); mBgArcPaint.setcolor(mBgArccolor); mDialPaint = new Paint(); mDialPaint.setAntiAlias(antiAlias); mDialPaint.setcolor(mDialcolor); mDialPaint.setstrokeWIDth(mDialWIDth); } /** * 更新圆弧画笔 */ private voID updateArcPaint() { // 设置渐变 // 渐变的颜色是360度,如果只显示270,那么则会缺失部分颜色 SweepGradIEnt sweepGradIEnt = new SweepGradIEnt(mCenterPoint.x, mCenterPoint.y, mGradIEntcolors, null); marcPaint.setShader(sweepGradIEnt); } @OverrIDe protected voID onMeasure(int wIDthMeasureSpec, int heightmeasureSpec) { super.onMeasure(wIDthMeasureSpec, heightmeasureSpec); setMeasuredDimension(MiscUtil.measure(wIDthMeasureSpec, mDefaultSize), MiscUtil.measure(heightmeasureSpec, mDefaultSize)); } @OverrIDe protected voID onSizeChanged(int w, int h, int olDW, int oldh) { super.onSizeChanged(w, h, olDW, oldh); Log.d(TAG, "onSizeChanged: w = " + w + "; h = " + h + "; olDW = " + olDW + "; oldh = " + oldh); int minSize = Math.min(getMeasureDWIDth() - getpaddingleft() - getpaddingRight() - 2 * (int) marcWIDth, getMeasuredHeight() - getpaddingtop() - getpaddingBottom() - 2 * (int) marcWIDth); mRadius = minSize / 2; mCenterPoint.x = getMeasureDWIDth() / 2; mCenterPoint.y = getMeasuredHeight() / 2; //绘制圆弧的边界 mRectF.left = mCenterPoint.x - mRadius - marcWIDth / 2; mRectF.top = mCenterPoint.y - mRadius - marcWIDth / 2; mRectF.right = mCenterPoint.x + mRadius + marcWIDth / 2; mRectF.bottom = mCenterPoint.y + mRadius + marcWIDth / 2; mValueOffset = mCenterPoint.y + getBaselineOffsetFromY(mValuePaint); mHintOffset = mCenterPoint.y - mRadius * mTextOffsetPercentInRadius + getBaselineOffsetFromY(mHintPaint); munitOffset = mCenterPoint.y + mRadius * mTextOffsetPercentInRadius + getBaselineOffsetFromY(munitPaint); updateArcPaint(); Log.d(TAG, "onMeasure: 控件大小 = " + "(" + getMeasureDWIDth() + ", " + getMeasuredHeight() + ")" + ";圆心坐标 = " + mCenterPoint.toString() + ";圆半径 = " + mRadius + ";圆的外接矩形 = " + mRectF.toString()); } private float getBaselineOffsetFromY(Paint paint) { return MiscUtil.measureTextHeight(paint) / 2; } @OverrIDe protected voID onDraw(Canvas canvas) { super.onDraw(canvas); drawArc(canvas); drawDial(canvas); drawText(canvas); } private voID drawArc(Canvas canvas) { // 绘制背景圆弧 // 从进度圆弧结束的地方开始重新绘制,优化性能 float currentAngle = mSweepAngle * mPercent; canvas.save(); canvas.rotate(mStartAngle, mCenterPoint.x, mCenterPoint.y); canvas.drawArc(mRectF, currentAngle, mSweepAngle - currentAngle, false, mBgArcPaint); // 第一个参数 oval 为 RectF 类型,即圆弧显示区域 // startAngle 和 sweepAngle 均为 float 类型,分别表示圆弧起始角度和圆弧度数 // 3点钟方向为0度,顺时针递增 // 如果 startAngle < 0 或者 > 360,则相当于 startAngle % 360 // useCenter:如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形 canvas.drawArc(mRectF, 0, currentAngle, false, marcPaint); canvas.restore(); } private voID drawDial(Canvas canvas) { int total = (int) (mSweepAngle / mDialintervalDegree); canvas.save(); canvas.rotate(mStartAngle, mCenterPoint.x, mCenterPoint.y); for (int i = 0; i <= total; i++) { canvas.drawline(mCenterPoint.x + mRadius, mCenterPoint.y, mCenterPoint.x + mRadius + marcWIDth, mCenterPoint.y, mDialPaint); canvas.rotate(mDialintervalDegree, mCenterPoint.x, mCenterPoint.y); } canvas.restore(); } private voID drawText(Canvas canvas) { canvas.drawText(String.format(mPrecisionFormat, mValue), mCenterPoint.x, mValueOffset, mValuePaint); if (munit != null) { canvas.drawText(munit.toString(), mCenterPoint.x, munitOffset, munitPaint); } if (mHint != null) { canvas.drawText(mHint.toString(), mCenterPoint.x, mHintOffset, mHintPaint); } } public float getMaxValue() { return mMaxValue; } public voID setMaxValue(float maxValue) { mMaxValue = maxValue; } /** * 设置当前值 * * @param value */ public voID setValue(float value) { if (value > mMaxValue) { value = mMaxValue; } float start = mPercent; float end = value / mMaxValue; startAnimator(start, end, mAnimTime); } private voID startAnimator(float start, float end, long animTime) { mAnimator = ValueAnimator.offloat(start, end); mAnimator.setDuration(animTime); mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @OverrIDe public voID onAnimationUpdate(ValueAnimator animation) { mPercent = (float) animation.getAnimatedValue(); mValue = mPercent * mMaxValue; if (BuildConfig.DEBUG) { Log.d(TAG, "onAnimationUpdate: percent = " + mPercent + ";currentAngle = " + (mSweepAngle * mPercent) + ";value = " + mValue); } invalIDate(); } }); mAnimator.start(); } public int[] getGradIEntcolors() { return mGradIEntcolors; } public voID setGradIEntcolors(int[] gradIEntcolors) { mGradIEntcolors = gradIEntcolors; updateArcPaint(); } public voID reset() { startAnimator(mPercent, 0.0f, 1000L); }}
import androID.animation.Animator;import androID.animation.ValueAnimator;import androID.annotation.TargetAPI;import androID.content.Context;import androID.content.res.TypedArray;import androID.graphics.Canvas;import androID.graphics.color;import androID.graphics.Paint;import androID.graphics.Path;import androID.graphics.Point;import androID.graphics.RectF;import androID.os.Build;import androID.text.TextPaint;import androID.util.AttributeSet;import androID.util.Log;import androID.vIEw.VIEw;import androID.vIEw.animation.linearInterpolator;import com.littlejIE.circleprogress.utils.Constant;import com.littlejIE.circleprogress.utils.MiscUtil;/** * 水波进度条 * Created by littlejIE on 2017/2/26. */public class WaveProgress extends VIEw { private static final String TAG = WaveProgress.class.getSimplename(); //浅色波浪方向 private static final int L2R = 0; private static final int R2L = 1; private int mDefaultSize; //圆心 private Point mCenterPoint; //半径 private float mRadius; //圆的外接矩形 private RectF mRectF; //深色波浪移动距离 private float mDarkWaveOffset; //浅色波浪移动距离 private float mlightWaveOffset; //浅色波浪方向 private boolean isR2L; //是否锁定波浪不随进度移动 private boolean lockWave; //是否开启抗锯齿 private boolean antiAlias; //最大值 private float mMaxValue; //当前值 private float mValue; //当前进度 private float mPercent; //绘制提示 private TextPaint mHintPaint; private CharSequence mHint; private int mHintcolor; private float mHintSize; private Paint mPercentPaint; private float mValueSize; private int mValuecolor; //圆环宽度 private float mCircleWIDth; //圆环 private Paint mCirclePaint; //圆环颜色 private int mCirclecolor; //背景圆环颜色 private int mBgCirclecolor; //水波路径 private Path mWavelimitPath; private Path mWavePath; //水波高度 private float mWaveHeight; //水波数量 private int mWaveNum; //深色水波 private Paint mWavePaint; //深色水波颜色 private int mDarkWavecolor; //浅色水波颜色 private int mlightWavecolor; //深色水波贝塞尔曲线上的起始点、控制点 private Point[] mDarkPoints; //浅色水波贝塞尔曲线上的起始点、控制点 private Point[] mlightPoints; //贝塞尔曲线点的总个数 private int mAllPointCount; private int mHalfPointCount; private ValueAnimator mProgressAnimator; private long mDarkWaveAnimTime; private ValueAnimator mDarkWaveAnimator; private long mlightWaveAnimTime; private ValueAnimator mlightWaveAnimator; public WaveProgress(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } private voID init(Context context, AttributeSet attrs) { mDefaultSize = MiscUtil.diptopx(context, Constant.DEFAulT_SIZE); mRectF = new RectF(); mCenterPoint = new Point(); initAttrs(context, attrs); initPaint(); initPath(); } private voID initAttrs(Context context, AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.WaveProgress); antiAlias = typedArray.getBoolean(R.styleable.WaveProgress_antiAlias, true); mDarkWaveAnimTime = typedArray.getInt(R.styleable.WaveProgress_darkWaveAnimTime, Constant.DEFAulT_ANIM_TIME); mlightWaveAnimTime = typedArray.getInt(R.styleable.WaveProgress_lightWaveAnimTime, Constant.DEFAulT_ANIM_TIME); mMaxValue = typedArray.getfloat(R.styleable.WaveProgress_maxValue, Constant.DEFAulT_MAX_VALUE); mValue = typedArray.getfloat(R.styleable.WaveProgress_value, Constant.DEFAulT_VALUE); mValueSize = typedArray.getDimension(R.styleable.WaveProgress_valueSize, Constant.DEFAulT_VALUE_SIZE); mValuecolor = typedArray.getcolor(R.styleable.WaveProgress_valuecolor, color.BLACK); mHint = typedArray.getString(R.styleable.WaveProgress_hint); mHintcolor = typedArray.getcolor(R.styleable.WaveProgress_hintcolor, color.BLACK); mHintSize = typedArray.getDimension(R.styleable.WaveProgress_hintSize, Constant.DEFAulT_HINT_SIZE); mCircleWIDth = typedArray.getDimension(R.styleable.WaveProgress_circleWIDth, Constant.DEFAulT_ARC_WIDTH); mCirclecolor = typedArray.getcolor(R.styleable.WaveProgress_circlecolor, color.GREEN); mBgCirclecolor = typedArray.getcolor(R.styleable.WaveProgress_bgCirclecolor, color.WHITE); mWaveHeight = typedArray.getDimension(R.styleable.WaveProgress_waveHeight, Constant.DEFAulT_WAVE_HEIGHT); mWaveNum = typedArray.getInt(R.styleable.WaveProgress_waveNum, 1); mDarkWavecolor = typedArray.getcolor(R.styleable.WaveProgress_darkWavecolor, getResources().getcolor(androID.R.color.holo_blue_dark)); mlightWavecolor = typedArray.getcolor(R.styleable.WaveProgress_lightWavecolor, getResources().getcolor(androID.R.color.holo_green_light)); isR2L = typedArray.getInt(R.styleable.WaveProgress_lightWaveDirect, R2L) == R2L; lockWave = typedArray.getBoolean(R.styleable.WaveProgress_lockWave, false); typedArray.recycle(); } private voID initPaint() { mHintPaint = new TextPaint(); // 设置抗锯齿,会消耗较大资源,绘制图形速度会变慢。 mHintPaint.setAntiAlias(antiAlias); // 设置绘制文字大小 mHintPaint.setTextSize(mHintSize); // 设置画笔颜色 mHintPaint.setcolor(mHintcolor); // 从中间向两边绘制,不需要再次计算文字 mHintPaint.setTextAlign(Paint.Align.CENTER); mCirclePaint = new Paint(); mCirclePaint.setAntiAlias(antiAlias); mCirclePaint.setstrokeWIDth(mCircleWIDth); mCirclePaint.setStyle(Paint.Style.stroke); mCirclePaint.setstrokeCap(Paint.Cap.ROUND); mWavePaint = new Paint(); mWavePaint.setAntiAlias(antiAlias); mWavePaint.setStyle(Paint.Style.FILL); mPercentPaint = new Paint(); mPercentPaint.setTextAlign(Paint.Align.CENTER); mPercentPaint.setAntiAlias(antiAlias); mPercentPaint.setcolor(mValuecolor); mPercentPaint.setTextSize(mValueSize); } private voID initPath() { mWavelimitPath = new Path(); mWavePath = new Path(); } @OverrIDe protected voID onMeasure(int wIDthMeasureSpec, int heightmeasureSpec) { super.onMeasure(wIDthMeasureSpec, heightmeasureSpec); setMeasuredDimension(MiscUtil.measure(wIDthMeasureSpec, mDefaultSize), MiscUtil.measure(heightmeasureSpec, mDefaultSize)); } @OverrIDe protected voID onSizeChanged(int w, int h, int olDW, int oldh) { super.onSizeChanged(w, h, olDW, oldh); Log.d(TAG, "onSizeChanged: w = " + w + "; h = " + h + "; olDW = " + olDW + "; oldh = " + oldh); int minSize = Math.min(getMeasureDWIDth() - getpaddingleft() - getpaddingRight() - 2 * (int) mCircleWIDth, getMeasuredHeight() - getpaddingtop() - getpaddingBottom() - 2 * (int) mCircleWIDth); mRadius = minSize / 2; mCenterPoint.x = getMeasureDWIDth() / 2; mCenterPoint.y = getMeasuredHeight() / 2; //绘制圆弧的边界 mRectF.left = mCenterPoint.x - mRadius - mCircleWIDth / 2; mRectF.top = mCenterPoint.y - mRadius - mCircleWIDth / 2; mRectF.right = mCenterPoint.x + mRadius + mCircleWIDth / 2; mRectF.bottom = mCenterPoint.y + mRadius + mCircleWIDth / 2; Log.d(TAG, "onSizeChanged: 控件大小 = " + "(" + getMeasureDWIDth() + ", " + getMeasuredHeight() + ")" + ";圆心坐标 = " + mCenterPoint.toString() + ";圆半径 = " + mRadius + ";圆的外接矩形 = " + mRectF.toString()); initWavePoints(); //开始动画 setValue(mValue); startWaveAnimator(); } private voID initWavePoints() { //当前波浪宽度 float waveWIDth = (mRadius * 2) / mWaveNum; mAllPointCount = 8 * mWaveNum + 1; mHalfPointCount = mAllPointCount / 2; mDarkPoints = getPoint(false, waveWIDth); mlightPoints = getPoint(isR2L, waveWIDth); } /** * 从左往右或者从右往左获取贝塞尔点 * * @return */ private Point[] getPoint(boolean isR2L, float waveWIDth) { Point[] points = new Point[mAllPointCount]; //第1个点特殊处理,即数组的中点 points[mHalfPointCount] = new Point((int) (mCenterPoint.x + (isR2L ? mRadius : -mRadius)), mCenterPoint.y); //屏幕内的贝塞尔曲线点 for (int i = mHalfPointCount + 1; i < mAllPointCount; i += 4) { float wIDth = points[mHalfPointCount].x + waveWIDth * (i / 4 - mWaveNum); points[i] = new Point((int) (waveWIDth / 4 + wIDth), (int) (mCenterPoint.y - mWaveHeight)); points[i + 1] = new Point((int) (waveWIDth / 2 + wIDth), mCenterPoint.y); points[i + 2] = new Point((int) (waveWIDth * 3 / 4 + wIDth), (int) (mCenterPoint.y + mWaveHeight)); points[i + 3] = new Point((int) (waveWIDth + wIDth), mCenterPoint.y); } //屏幕外的贝塞尔曲线点 for (int i = 0; i < mHalfPointCount; i++) { int reverse = mAllPointCount - i - 1; points[i] = new Point((isR2L ? 2 : 1) * points[mHalfPointCount].x - points[reverse].x, points[mHalfPointCount].y * 2 - points[reverse].y); } //对从右向左的贝塞尔点数组反序,方便后续处理 return isR2L ? MiscUtil.reverse(points) : points; } @OverrIDe protected voID onDraw(Canvas canvas) { super.onDraw(canvas); drawCircle(canvas); drawlightWave(canvas); drawDarkWave(canvas); drawProgress(canvas); } /** * 绘制圆环 * * @param canvas */ private voID drawCircle(Canvas canvas) { canvas.save(); canvas.rotate(270, mCenterPoint.x, mCenterPoint.y); int currentAngle = (int) (360 * mPercent); //画背景圆环 mCirclePaint.setcolor(mBgCirclecolor); canvas.drawArc(mRectF, currentAngle, 360 - currentAngle, false, mCirclePaint); //画圆环 mCirclePaint.setcolor(mCirclecolor); canvas.drawArc(mRectF, 0, currentAngle, false, mCirclePaint); canvas.restore(); } /** * 绘制深色波浪(贝塞尔曲线) * * @param canvas */ private voID drawDarkWave(Canvas canvas) { mWavePaint.setcolor(mDarkWavecolor); drawWave(canvas, mWavePaint, mDarkPoints, mDarkWaveOffset); } /** * 绘制浅色波浪(贝塞尔曲线) * * @param canvas */ private voID drawlightWave(Canvas canvas) { mWavePaint.setcolor(mlightWavecolor); //从右向左的水波位移应该被减去 drawWave(canvas, mWavePaint, mlightPoints, isR2L ? -mlightWaveOffset : mlightWaveOffset); } @TargetAPI(Build.VERSION_CODES.KITKAT) private voID drawWave(Canvas canvas, Paint paint, Point[] points, float waveOffset) { mWavelimitPath.reset(); mWavePath.reset(); float height = lockWave ? 0 : mRadius - 2 * mRadius * mPercent; //moveto和lineto绘制出水波区域矩形 mWavePath.moveto(points[0].x + waveOffset, points[0].y + height); for (int i = 1; i < mAllPointCount; i += 2) { mWavePath.quadTo(points[i].x + waveOffset, points[i].y + height, points[i + 1].x + waveOffset, points[i + 1].y + height); } //mWavePath.lineto(points[mAllPointCount - 1].x, points[mAllPointCount - 1].y + height); //不管如何移动,波浪与圆路径的交集底部永远固定,否则会造成上移的时候底部为空的情况 mWavePath.lineto(points[mAllPointCount - 1].x, mCenterPoint.y + mRadius); mWavePath.lineto(points[0].x, mCenterPoint.y + mRadius); mWavePath.close(); mWavelimitPath.addCircle(mCenterPoint.x, mCenterPoint.y, mRadius, Path.Direction.CW); //取该圆与波浪路径的交集,形成波浪在圆内的效果 mWavelimitPath.op(mWavePath, Path.Op.INTERSECT); canvas.drawPath(mWavelimitPath, paint); } //前一次绘制时的进度 private float mPrePercent; //当前进度值 private String mPercentValue; private voID drawProgress(Canvas canvas) { float y = mCenterPoint.y - (mPercentPaint.descent() + mPercentPaint.ascent()) / 2; if (BuildConfig.DEBUG) { Log.d(TAG, "mPercent = " + mPercent + "; mPrePercent = " + mPrePercent); } if (mPrePercent == 0.0f || Math.abs(mPercent - mPrePercent) >= 0.01f) { mPercentValue = String.format("%.0f%%", mPercent * 100); mPrePercent = mPercent; } canvas.drawText(mPercentValue, mCenterPoint.x, y, mPercentPaint); if (mHint != null) { float hy = mCenterPoint.y * 2 / 3 - (mHintPaint.descent() + mHintPaint.ascent()) / 2; canvas.drawText(mHint.toString(), mCenterPoint.x, hy, mHintPaint); } } public float getMaxValue() { return mMaxValue; } public voID setMaxValue(float maxValue) { mMaxValue = maxValue; } public float getValue() { return mValue; } /** * 设置当前值 * * @param value */ public voID setValue(float value) { if (value > mMaxValue) { value = mMaxValue; } float start = mPercent; float end = value / mMaxValue; Log.d(TAG, "setValue, value = " + value + ";start = " + start + "; end = " + end); startAnimator(start, end, mDarkWaveAnimTime); } private voID startAnimator(final float start, float end, long animTime) { Log.d(TAG, "startAnimator,value = " + mValue + ";start = " + start + ";end = " + end + ";time = " + animTime); //当start=0且end=0时,不需要启动动画 if (start == 0 && end == 0) { return; } mProgressAnimator = ValueAnimator.offloat(start, end); mProgressAnimator.setDuration(animTime); mProgressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @OverrIDe public voID onAnimationUpdate(ValueAnimator animation) { mPercent = (float) animation.getAnimatedValue(); if (mPercent == 0.0f || mPercent == 1.0f) { stopWaveAnimator(); } else { startWaveAnimator(); } mValue = mPercent * mMaxValue; if (BuildConfig.DEBUG) { Log.d(TAG, "onAnimationUpdate: percent = " + mPercent + ";value = " + mValue); } invalIDate(); } }); mProgressAnimator.start(); } private voID startWaveAnimator() { startlightWaveAnimator(); startDarkWaveAnimator(); } private voID stopWaveAnimator() { if (mDarkWaveAnimator != null && mDarkWaveAnimator.isRunning()) { mDarkWaveAnimator.cancel(); mDarkWaveAnimator.removeAllUpdateListeners(); mDarkWaveAnimator = null; } if (mlightWaveAnimator != null && mlightWaveAnimator.isRunning()) { mlightWaveAnimator.cancel(); mlightWaveAnimator.removeAllUpdateListeners(); mlightWaveAnimator = null; } } private voID startlightWaveAnimator() { if (mlightWaveAnimator != null && mlightWaveAnimator.isRunning()) { return; } mlightWaveAnimator = ValueAnimator.offloat(0, 2 * mRadius); mlightWaveAnimator.setDuration(mlightWaveAnimTime); mlightWaveAnimator.setRepeatCount(ValueAnimator.INFINITE); mlightWaveAnimator.setInterpolator(new linearInterpolator()); mlightWaveAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @OverrIDe public voID onAnimationUpdate(ValueAnimator animation) { mlightWaveOffset = (float) animation.getAnimatedValue(); postInvalIDate(); } }); mlightWaveAnimator.addListener(new Animator.AnimatorListener() { @OverrIDe public voID onAnimationStart(Animator animation) { mlightWaveOffset = 0; } @OverrIDe public voID onAnimationEnd(Animator animation) { } @OverrIDe public voID onAnimationCancel(Animator animation) { } @OverrIDe public voID onAnimationRepeat(Animator animation) { } }); mlightWaveAnimator.start(); } private voID startDarkWaveAnimator() { if (mDarkWaveAnimator != null && mDarkWaveAnimator.isRunning()) { return; } mDarkWaveAnimator = ValueAnimator.offloat(0, 2 * mRadius); mDarkWaveAnimator.setDuration(mDarkWaveAnimTime); mDarkWaveAnimator.setRepeatCount(ValueAnimator.INFINITE); mDarkWaveAnimator.setInterpolator(new linearInterpolator()); mDarkWaveAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @OverrIDe public voID onAnimationUpdate(ValueAnimator animation) { mDarkWaveOffset = (float) animation.getAnimatedValue(); postInvalIDate(); } }); mDarkWaveAnimator.addListener(new Animator.AnimatorListener() { @OverrIDe public voID onAnimationStart(Animator animation) { mDarkWaveOffset = 0; } @OverrIDe public voID onAnimationEnd(Animator animation) { } @OverrIDe public voID onAnimationCancel(Animator animation) { } @OverrIDe public voID onAnimationRepeat(Animator animation) { } }); mDarkWaveAnimator.start(); } @OverrIDe protected voID onDetachedFromWindow() { super.onDetachedFromWindow(); stopWaveAnimator(); if (mProgressAnimator != null && mProgressAnimator.isRunning()) { mProgressAnimator.cancel(); mProgressAnimator.removeAllUpdateListeners(); mProgressAnimator = null; } }}
/** * Created by littlejIE on 2017/2/26. */public class Constant { public static final boolean ANTI_AliAS = true; public static final int DEFAulT_SIZE = 150; public static final int DEFAulT_START_ANGLE = 270; public static final int DEFAulT_SWEEP_ANGLE = 360; public static final int DEFAulT_ANIM_TIME = 1000; public static final int DEFAulT_MAX_VALUE = 100; public static final int DEFAulT_VALUE = 50; public static final int DEFAulT_HINT_SIZE = 15; public static final int DEFAulT_UNIT_SIZE = 30; public static final int DEFAulT_VALUE_SIZE = 15; public static final int DEFAulT_ARC_WIDTH = 15; public static final int DEFAulT_WAVE_HEIGHT = 40;}
import androID.content.Context;import androID.graphics.Paint;import androID.vIEw.VIEw;/** * Created by littlejIE on 2017/2/22. */public class MiscUtil { /** * 测量 VIEw * * @param measureSpec * @param defaultSize VIEw 的默认大小 * @return */ public static int measure(int measureSpec, int defaultSize) { int result = defaultSize; int specMode = VIEw.MeasureSpec.getMode(measureSpec); int specsize = VIEw.MeasureSpec.getSize(measureSpec); if (specMode == VIEw.MeasureSpec.EXACTLY) { result = specsize; } else if (specMode == VIEw.MeasureSpec.AT_MOST) { result = Math.min(result, specsize); } return result; } /** * dip 转换成px * * @param dip * @return */ public static int diptopx(Context context, float dip) { float density = context.getResources().getdisplayMetrics().density; return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1)); } /** * 获取数值精度格式化字符串 * * @param precision * @return */ public static String getPrecisionFormat(int precision) { return "%." + precision + "f"; } /** * 反转数组 * * @param arrays * @param <T> * @return */ public static <T> T[] reverse(T[] arrays) { if (arrays == null) { return null; } int length = arrays.length; for (int i = 0; i < length / 2; i++) { T t = arrays[i]; arrays[i] = arrays[length - i - 1]; arrays[length - i - 1] = t; } return arrays; } /** * 测量文字高度 * @param paint * @return */ public static float measureTextHeight(Paint paint) { Paint.FontMetrics FontMetrics = paint.getFontMetrics(); return (Math.abs(FontMetrics.ascent) - FontMetrics.descent); }}
<!-- 是否开启抗锯齿 --> <attr name="antiAlias" format="boolean" /> <!-- 圆弧起始角度,3点钟方向为0,顺时针递增,小于0或大于360进行取余 --> <attr name="startAngle" format="float" /> <!-- 圆弧度数 --> <attr name="sweepAngle" format="float" /> <!-- 设置动画时间 --> <attr name="animTime" format="integer" /> <!-- 绘制内容的数值 --> <attr name="maxValue" format="float" /> <attr name="value" format="float" /> <!-- 绘制内容的单位 --> <attr name="unit" format="string|reference" /> <attr name="unitSize" format="dimension" /> <attr name="unitcolor" format="color|reference" /> <!-- 绘制内容相应的提示语 --> <attr name="hint" format="string|reference" /> <attr name="hintSize" format="dimension" /> <attr name="hintcolor" format="color|reference" /> <!-- 精度,默认为0 --> <attr name="precision" format="integer" /> <attr name="valueSize" format="dimension" /> <attr name="valuecolor" format="color|reference" /> <!-- 圆弧颜色,设置多个可实现渐变 --> <attr name="arccolor1" format="color|reference" /> <attr name="arccolor2" format="color|reference" /> <attr name="arccolor3" format="color|reference" /> <!-- 背景圆弧颜色,默认白色 --> <attr name="bgArccolor" format="color|reference" /> <!-- 圆弧宽度 --> <attr name="arcWIDth" format="dimension" /> <!-- 圆弧颜色, --> <attr name="arccolors" format="color|reference" /> <!-- 文字的偏移量。相对于圆半径而言,默认三分之一 --> <attr name="textOffsetPercentInRadius" format="float" /> <!-- 圆形进度条 --> <declare-styleable name="CircleProgressbar"> <attr name="antiAlias" /> <attr name="startAngle" /> <attr name="sweepAngle" /> <attr name="animTime" /> <attr name="maxValue" /> <attr name="value" /> <attr name="precision" /> <attr name="valueSize" /> <attr name="valuecolor" /> <attr name="textOffsetPercentInRadius" /> <!-- 绘制内容相应的提示语 --> <attr name="hint" /> <attr name="hintSize" /> <attr name="hintcolor" /> <!-- 绘制内容的单位 --> <attr name="unit" /> <attr name="unitSize" /> <attr name="unitcolor" /> <!-- 圆弧宽度 --> <attr name="arcWIDth" /> <attr name="arccolors" /> <!-- 背景圆弧颜色 --> <attr name="bgArccolor" /> <!-- 背景圆弧宽度 --> <attr name="bgArcWIDth" format="dimension" /> </declare-styleable> <declare-styleable name="DialProgress"> <attr name="antiAlias" /> <attr name="startAngle" /> <attr name="sweepAngle" /> <attr name="animTime" /> <attr name="maxValue" /> <attr name="value" /> <attr name="precision" /> <attr name="valueSize" /> <attr name="valuecolor" /> <attr name="textOffsetPercentInRadius" /> <!-- 绘制内容的单位 --> <attr name="unit" /> <attr name="unitSize" /> <attr name="unitcolor" /> <!-- 绘制内容相应的提示语 --> <attr name="hint" /> <attr name="hintSize" /> <attr name="hintcolor" /> <!-- 圆弧的宽度 --> <attr name="arcWIDth" /> <!-- 刻度的宽度 --> <attr name="dialWIDth" format="dimension|reference" /> <!-- 刻度之间的间隔 --> <attr name="dialintervalDegree" format="integer" /> <!-- 圆弧颜色, --> <attr name="arccolors" /> <!-- 背景圆弧线颜色 --> <attr name="bgArccolor" /> <!-- 刻度线颜色 --> <attr name="dialcolor" format="color|reference" /> </declare-styleable> <declare-styleable name="WaveProgress"> <!-- 是否开启抗锯齿 --> <attr name="antiAlias" /> <!-- 深色水波动画时间 --> <attr name="darkWaveAnimTime" format="integer" /> <!-- 浅色水波动画时间 --> <attr name="lightWaveAnimTime" format="integer" /> <!-- 最大值 --> <attr name="maxValue" /> <!-- 当前值 --> <attr name="value" /> <attr name="valuecolor" /> <attr name="valueSize" /> <!-- 绘制内容相应的提示语 --> <attr name="hint" /> <attr name="hintSize" /> <attr name="hintcolor" /> <!-- 圆环宽度 --> <attr name="circleWIDth" format="dimension" /> <!-- 圆环颜色 --> <attr name="circlecolor" format="color|reference" /> <!-- 背景圆环颜色 --> <attr name="bgCirclecolor" format="color|reference" /> <!-- 锁定水波不随圆环进度改变,默认锁定在50%处 --> <attr name="lockWave" format="boolean" /> <!-- 水波数量 --> <attr name="waveNum" format="integer" /> <!-- 水波高度,峰值和谷值之和 --> <attr name="waveHeight" format="dimension" /> <!-- 深色水波颜色 --> <attr name="darkWavecolor" format="color|reference" /> <!-- 是否显示浅色水波 --> <attr name="showlightWave" format="boolean" /> <!-- 浅色水波颜色 --> <attr name="lightWavecolor" format="color|reference" /> <!-- 浅色水波的方向 --> <attr name="lightWaveDirect" format="enum"> <enum name="L2R" value="0" /> <enum name="R2L" value="1" /> </attr> </declare-styleable>
<dimen name="small">5dp</dimen> <dimen name="medium">10dp</dimen> <dimen name="normal">15dp</dimen> <dimen name="large">20dp</dimen> <dimen name="xlarge">25dp</dimen> <dimen name="xxlarge">30dp</dimen> <!-- text size --> <dimen name="text_size_35">35sp</dimen> <dimen name="text_size_34">34sp</dimen> <dimen name="text_size_33">33sp</dimen> <dimen name="text_size_32">32sp</dimen> <dimen name="text_size_31">31sp</dimen> <dimen name="text_size_30">30sp</dimen> <dimen name="text_size_29">29sp</dimen> <dimen name="text_size_28">28sp</dimen> <dimen name="text_size_26">26sp</dimen> <dimen name="text_size_25">25sp</dimen> <dimen name="text_size_24">24sp</dimen> <dimen name="text_size_23">23sp</dimen> <dimen name="text_size_22">22sp</dimen> <dimen name="text_size_21">21sp</dimen> <dimen name="text_size_20">20sp</dimen> <dimen name="text_size_19">19sp</dimen> <dimen name="text_size_18">18sp</dimen> <dimen name="text_size_17">17sp</dimen> <dimen name="text_size_16">16sp</dimen> <dimen name="text_size_15">15sp</dimen> <dimen name="text_size_14">14sp</dimen> <dimen name="text_size_13">13sp</dimen> <dimen name="text_size_12">12sp</dimen> <dimen name="text_size_11">11sp</dimen> <dimen name="text_size_10">10sp</dimen> <dimen name="text_size_9">9sp</dimen> <dimen name="text_size_8">8sp</dimen> <dimen name="text_size_7">7sp</dimen>
<?xml version="1.0" enCoding="utf-8"?><ScrollVIEw xmlns:androID="http://schemas.androID.com/apk/res/androID" xmlns:app="http://schemas.androID.com/apk/res-auto" xmlns:tools="http://schemas.androID.com/tools" androID:ID="@+ID/activity_main" androID:layout_wIDth="match_parent" androID:layout_height="match_parent" tools:context="com.littlejIE.app.MainActivity"> <linearLayout androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:orIEntation="vertical"> <button androID:ID="@+ID/btn_reset_all" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:text="重置" /> <com.littlejIE.circleprogress.CircleProgress androID:ID="@+ID/circle_progress_bar1" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:layout_gravity="center_horizontal" app:antiAlias="true" app:arcWIDth="@dimen/small" app:bgArccolor="@color/colorAccent" app:bgArcWIDth="@dimen/small" app:hint="截止当前已走" app:hintSize="15sp" app:maxValue="10000" app:startAngle="135" app:sweepAngle="270" app:unit="步" app:unitSize="15sp" app:value="10000" app:valueSize="25sp" /> <com.littlejIE.circleprogress.CircleProgress androID:ID="@+ID/circle_progress_bar2" androID:layout_wIDth="100dp" androID:layout_height="200dp" androID:layout_gravity="center_horizontal" app:antiAlias="true" app:arcWIDth="@dimen/small" app:bgArccolor="@color/colorAccent" app:bgArcWIDth="@dimen/small" app:hint="百分比" app:hintSize="@dimen/text_size_15" app:maxValue="100" app:startAngle="135" app:sweepAngle="270" app:textOffsetPercentInRadius="0.5" app:unit="%" app:unitSize="@dimen/text_size_15" app:value="75" app:valueSize="@dimen/text_size_20" /> <com.littlejIE.circleprogress.CircleProgress androID:ID="@+ID/circle_progress_bar3" androID:layout_wIDth="200dp" androID:layout_height="200dp" androID:layout_gravity="center_horizontal" app:antiAlias="true" app:arcWIDth="@dimen/small" app:bgArccolor="@androID:color/darker_gray" app:bgArcWIDth="@dimen/small" app:hint="当前进度" app:hintSize="@dimen/text_size_25" app:maxValue="100" app:startAngle="270" app:sweepAngle="360" app:unit="%" app:unitSize="@dimen/text_size_25" app:value="100" app:valueSize="@dimen/text_size_35" /> <com.littlejIE.circleprogress.DialProgress androID:ID="@+ID/dial_progress_bar" androID:layout_wIDth="300dp" androID:layout_height="300dp" androID:layout_gravity="center_horizontal" androID:padding="@dimen/medium" app:animTime="1000" app:arccolors="@array/gradIEnt_arc_color" app:arcWIDth="@dimen/large" app:dialintervalDegree="3" app:dialWIDth="2dp" app:hint="当前时速" app:hintSize="@dimen/text_size_25" app:maxValue="360" app:startAngle="270" app:sweepAngle="360" app:unit="km/h" app:unitSize="@dimen/text_size_25" app:value="360" app:valueSize="@dimen/text_size_35" /> <com.littlejIE.circleprogress.WaveProgress androID:ID="@+ID/wave_progress_bar" androID:layout_wIDth="300dp" androID:layout_height="300dp" androID:layout_gravity="center_horizontal" app:darkWaveAnimTime="1000" app:darkWavecolor="@color/dark" app:lightWaveAnimTime="2000" app:lightWavecolor="@color/light" app:lightWaveDirect="R2L" app:lockWave="false" app:valueSize="@dimen/text_size_35" app:waveHeight="30dp" app:waveNum="1" /> </linearLayout></ScrollVIEw>
使用方法
public class MainActivity extends AppCompatActivity implements VIEw.OnClickListener { private final static int[] colorS = new int[]{color.RED, color.YELLOW, color.GREEN, color.BLUE}; private button mBtnresetAll; private CircleProgress mCircleProgress1, mCircleProgress2, mCircleProgress3; private DialProgress mDialProgress; private WaveProgress mWaveProgress; private Random mRandom; @OverrIDe protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.activity_main); mBtnresetAll = (button) findVIEwByID(R.ID.btn_reset_all); mCircleProgress1 = (CircleProgress) findVIEwByID(R.ID.circle_progress_bar1); mCircleProgress2 = (CircleProgress) findVIEwByID(R.ID.circle_progress_bar2); mCircleProgress3 = (CircleProgress) findVIEwByID(R.ID.circle_progress_bar3); mDialProgress = (DialProgress) findVIEwByID(R.ID.dial_progress_bar); mWaveProgress = (WaveProgress) findVIEwByID(R.ID.wave_progress_bar); mBtnresetAll.setonClickListener(this); mCircleProgress1.setonClickListener(this); mCircleProgress2.setonClickListener(this); mCircleProgress3.setonClickListener(this); mDialProgress.setonClickListener(this); mWaveProgress.setonClickListener(this); mRandom = new Random(); } @OverrIDe public voID onClick(VIEw v) { switch (v.getID()) { case R.ID.btn_reset_all: mCircleProgress1.reset(); mCircleProgress2.reset(); mCircleProgress3.reset(); mDialProgress.reset(); break; case R.ID.circle_progress_bar1: mCircleProgress1.setValue(mRandom.nextInt((int) mCircleProgress1.getMaxValue())); break; case R.ID.circle_progress_bar2: mCircleProgress2.setValue(mRandom.nextfloat() * mCircleProgress2.getMaxValue()); break; case R.ID.circle_progress_bar3: //在代码中动态改变渐变色,可能会导致颜色跳跃 mCircleProgress3.setGradIEntcolors(colorS); mCircleProgress3.setValue(mRandom.nextfloat() * mCircleProgress3.getMaxValue()); break; case R.ID.dial_progress_bar: mDialProgress.setValue(mRandom.nextfloat() * mDialProgress.getMaxValue()); break; case R.ID.wave_progress_bar: mWaveProgress.setValue(mRandom.nextfloat() * mWaveProgress.getMaxValue()); break; } }}
color.xml
<color name="dark">#803cbcb7</color> <color name="light">#800de6e8</color> <color name="green">#00FF00</color> <color name="blue">#EE9A00</color> <color name="red">#EE0000</color> <integer-array name="gradIEnt_arc_color"> <item>@color/green</item> <item>@color/blue</item> <item>@color/red</item> </integer-array>
dimens.xml
<dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen>
git传送阵
总结以上是内存溢出为你收集整理的Android进度条升级篇全部内容,希望文章能够帮你解决Android进度条升级篇所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)