简单的自定义进度view
2018-10-25 本文已影响7人
RoyAlex
首先附上效果图

import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.support.annotation.Size;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
//进度view用动画来表达进度:成功、失败、进行中、倒计
public class ProgressView extends View {
//颜色
private int progressed_color, unProgressed_color, success_color, failure_color, solidColor;
//尺寸
private int circle_radius = 50, circle_thickness, result_thickness;
//默认颜色
private static int DEFAULT_COLOR = Color.WHITE;
//数据
private int currentProgress, totalProgress = 100;
//状态 0 processing 1 success 2 failure
@Size(min = 0, max = 360)
private int arcAngle = 40;
@PV_STATUS
private int status = PV_STATUS.success;
private RectF rectF;
//画笔
private Paint mProgressPaint, mSuccessPaint, mFailurePaint, mSolidPaint;
public ProgressView(Context context) {
this(context, null);
}
public ProgressView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProgressView);
progressed_color = array.getColor(R.styleable.ProgressView_progressedColor, Color.GREEN);
unProgressed_color = array.getColor(R.styleable.ProgressView_unProgressedColor, DEFAULT_COLOR);
success_color = array.getColor(R.styleable.ProgressView_successColor, Color.WHITE);
failure_color = array.getColor(R.styleable.ProgressView_failureColor, Color.RED);
circle_radius = array.getDimensionPixelOffset((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
R.styleable.ProgressView_radius, context.getResources().getDisplayMetrics()), 50);
circle_thickness = array.getDimensionPixelOffset((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
R.styleable.ProgressView_thickness, context.getResources().getDisplayMetrics()), 22);
result_thickness = array.getDimensionPixelOffset((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
R.styleable.ProgressView_statusThickness, context.getResources().getDisplayMetrics()), 18);
solidColor = array.getColor(R.styleable.ProgressView_fillColor, Color.GREEN);
totalProgress = array.getInteger(R.styleable.ProgressView_totalProgress, 100);
array.recycle();
init();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(2*circle_radius + circle_thickness, 2*circle_radius + circle_thickness);
}
//初始化各项操作
private void init() {
mProgressPaint = new Paint();
mSuccessPaint = new Paint();
mFailurePaint = new Paint();
mSolidPaint = new Paint();
mSolidPaint.setColor(solidColor);
mSolidPaint.setAntiAlias(true);
mSolidPaint.setDither(true);
mSolidPaint.setStyle(Paint.Style.FILL);
mProgressPaint.setStrokeWidth(circle_thickness);
mProgressPaint.setAntiAlias(true);
mProgressPaint.setDither(true);
mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
mProgressPaint.setColor(progressed_color);
mSuccessPaint.setAntiAlias(true);
mSuccessPaint.setStrokeCap(Paint.Cap.ROUND);
mSuccessPaint.setDither(true);
mSuccessPaint.setColor(success_color);
mSuccessPaint.setStrokeWidth(result_thickness);
mSuccessPaint.setStyle(Paint.Style.STROKE);
mSuccessPaint.setPathEffect(new CornerPathEffect(45));
mFailurePaint.setColor(failure_color);
mFailurePaint.setStyle(Paint.Style.STROKE);
mFailurePaint.setStrokeWidth(result_thickness);
mFailurePaint.setAntiAlias(true);
mFailurePaint.setStrokeCap(Paint.Cap.ROUND);
mFailurePaint.setDither(true);
mFailurePaint.setStrokeWidth(result_thickness);
rectF = new RectF(circle_thickness/2, circle_thickness/2, circle_radius * 2 + circle_thickness/2,
circle_radius * 2 + circle_thickness/2);
successPath = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawCircle(circle_radius + circle_thickness/2, circle_radius + circle_thickness/2,
circle_radius - circle_thickness/2, mSolidPaint);
switch (status) {
default:
break;
case PV_STATUS.failure:
canvas.drawArc(rectF, -51.4f, failAngel
, false, mProgressPaint);
canvas.drawLine(0.4f * (circle_radius + circle_thickness/2),0.2f* (circle_radius + circle_thickness/2),
xLeft, 4f/3* xLeft -1f/3* (circle_radius + circle_thickness/2),mFailurePaint);
canvas.drawLine(0.4f * (circle_radius + circle_thickness/2),1.8f* (circle_radius + circle_thickness/2),
xLeft, 7f/3*(circle_radius + circle_thickness/2) - 4f/3* xLeft,mFailurePaint);
break;
case PV_STATUS.process:
float startAngle = (currentProgress * 360) / totalProgress;
canvas.drawArc(rectF, startAngle, startAngle + arcAngle
, false, mProgressPaint);
break;
case PV_STATUS.success:
canvas.drawArc(rectF, 180, successAngel
, false, mProgressPaint);
canvas.drawPath(successPath, mSuccessPaint);
break;
}
}
public void setStatus(@PV_STATUS int status) {
this.status = status;
switch (status) {
default:
break;
case PV_STATUS.failure:
mProgressPaint.setColor(Color.RED);
mFailurePaint.setColor(Color.WHITE);
mSolidPaint.setColor(Color.RED);
if(failureSet != null && failureSet.isRunning()){
failureSet.cancel();
}
startFailureAnimation();
break;
case PV_STATUS.process:
mSolidPaint.setColor(Color.GREEN);
mProgressPaint.setColor(Color.GREEN);
if (processAnimator != null && processAnimator.isRunning()) {
processAnimator.cancel();
}
startProcessAnimation();
break;
case PV_STATUS.success:
mProgressPaint.setColor(Color.GREEN);
mFailurePaint.setColor(Color.WHITE);
mSolidPaint.setColor(Color.GREEN);
if (successSet != null && successSet.isRunning()) {
successSet.cancel();
}
successPath.rewind();
successPath.moveTo(0, circle_radius);
startSuccessAnimation();
break;
}
}
ValueAnimator processAnimator;
AnimatorSet successSet;
private void startProcessAnimation() {
processAnimator = ValueAnimator.ofInt(0, totalProgress);
processAnimator.setInterpolator(new LinearInterpolator());
processAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
currentProgress = (int) valueAnimator.getAnimatedValue();
invalidate();
}
});
processAnimator.setDuration(2000);
processAnimator.setRepeatCount(ValueAnimator.INFINITE);
processAnimator.setRepeatMode(ValueAnimator.REVERSE);
processAnimator.start();
}
private Path successPath;
private int successAngel = 0,failAngel = 0;
private void startSuccessAnimation() {
successSet = new AnimatorSet();
ValueAnimator process = ValueAnimator.ofInt(0,360);
ValueAnimator suXPathAnimator = ValueAnimator.ofFloat(0f + circle_thickness/2, 0.8f * (circle_radius+circle_thickness/2), 1.8f * (circle_radius+circle_thickness/2));
ValueAnimator suYPathAnimator = ValueAnimator.ofFloat(circle_radius + circle_thickness/2, 1.6f * (circle_radius+circle_thickness/2), 0.4f * (circle_radius+circle_thickness/2));
suYPathAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
successPath.lineTo((float) suXPathAnimator.getAnimatedValue(),
(float) suYPathAnimator.getAnimatedValue());
invalidate();
}
});
process.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
successAngel = (int) valueAnimator.getAnimatedValue();
}
});
successSet.setDuration(1000);
successSet.setInterpolator(new AccelerateDecelerateInterpolator());
successSet.play(suXPathAnimator)
.with(suYPathAnimator)
.with(process);
successSet.start();
}
private AnimatorSet failureSet;
private float xLeft =0;
private void startFailureAnimation() {
failureSet = new AnimatorSet();
ValueAnimator failureAnim = ValueAnimator.ofFloat(0.4f* (circle_radius
+ circle_thickness/2),1.6f* (circle_radius +circle_thickness/2));
failureAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
xLeft = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
ValueAnimator process = ValueAnimator.ofInt(0,360);
process.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
failAngel = (int) valueAnimator.getAnimatedValue();
}
});
failureSet.play(failureAnim)
.with(process);
failureSet.setDuration(1000);
failureSet.setInterpolator(new AccelerateDecelerateInterpolator());
failureSet.start();
}
@IntDef({PV_STATUS.process, PV_STATUS.success, PV_STATUS.failure})
@Retention(RetentionPolicy.SOURCE)
public @interface PV_STATUS {
int process = 0;
int success = 1;
int failure = 2;
}
}
附上