失败作品
public class CircleView extends View {
boolean isEnd = false;
private Path mPath;
private Paint mPaint;
private PathMeasure mPathMeasure;
private float mAnimatorValue;
private Path mDst;
private float mLength;
private Path mDuihaoPath;
private int width;
private Bitmap bitmapCanvas;
private Canvas canvas2;
private Paint mDuihaoPaint;
public CircleView(Context context) {
super(context);
}
public CircleView(Context context, AttributeSet attrs) {
super(context, attrs);
mPathMeasure = new PathMeasure();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(6);
mPaint.setTextSize(60);
// mPaint.setStrokeCap(Paint.Cap.BUTT);
//圆形的时候 有明显的圆形 开头/结尾
mPaint.setStrokeCap(Paint.Cap.ROUND);
// mPaint.setStrokeCap(Paint.Cap.SQUARE);
// mPaint.setStrokeJoin(Paint.Join.BEVEL);
// mPaint.setStrokeJoin(Paint.Join.MITER);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPath = new Path();
//200 200 这个点 就是这个圆形 中心坐标 这个坐标是相对于 View在XML 里的左上角的点为0 0
// mPath.addCircle(200, 200, 100, Path.Direction.CW);
width = ConvertUtils.dp2px(300);
mPath.addCircle(width / 2, width / 2, width / 2, Path.Direction.CW);// 顺时针转
// forceClosed Path最终是否需要闭合,如果为True的话,
// 则不管关联的Path是否是闭合的,都会被闭合。
mPathMeasure.setPath(mPath, true);
mLength = mPathMeasure.getLength();
mDst = new Path();
final ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mAnimatorValue = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
isEnd = true;
final ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mAnimatorValue = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
valueAnimator.start();
mDuihaoPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mDuihaoPaint.setStyle(Paint.Style.STROKE);
mDuihaoPaint.setStrokeWidth(6);
mDuihaoPaint.setTextSize(60);
bitmapCanvas = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
canvas2 = new Canvas(bitmapCanvas);
mDuihaoPath = new Path();
mDuihaoPath.moveTo(width * 0.2f, width * 0.5f);
mDuihaoPath.quadTo(width / 2f, width, width * 0.8f, width * 0.3f);
PathMeasure mPathMeasure = new PathMeasure(mDuihaoPath, false);
final float length = mPathMeasure.getLength();
ValueAnimator mAnimator = ValueAnimator.ofFloat(1, 0);
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.setDuration(2000);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float fraction = (float) animation.getAnimatedValue();
DashPathEffect mEffect = new DashPathEffect(new float[]{length, length}, fraction * length);
mDuihaoPaint.setPathEffect(mEffect);
invalidate();
}
});
mAnimator.start();
}
});
valueAnimator.setDuration(1000);
valueAnimator.setRepeatCount(2);
valueAnimator.start();
}
public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mDst.reset();
// 硬件加速的BUG 加上这个会在左上角留下一个黑点
// mDst.lineTo(0, 0);
float stop = mLength * mAnimatorValue;
if (isEnd) {
float start = (float) (stop - ((1 - Math.abs(mAnimatorValue - 1)) * mLength));
mPathMeasure.getSegment(start, stop, mDst, true);
canvas.drawPath(mDst, mPaint);
} else {
float start = (float) (stop - ((0.7 - Math.abs(mAnimatorValue - 0.7)) * mLength));
mPathMeasure.getSegment(start, stop, mDst, true);
canvas.drawPath(mDst, mPaint);
}
if(canvas2 != null){
canvas2.drawPath(mDuihaoPath, mDuihaoPaint);
canvas.drawBitmap(bitmapCanvas, 0,0,mDuihaoPaint);
}
}
}
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MdStyleProgress"> <attr name="progress_color" format="color"/> <attr name="progress_width" format="dimension"/> <attr name="radius" format="dimension"/> </declare-styleable> </resources>
package com.as.demo_ok75;
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.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
public class MdStyleProgress extends View {
private static final int PROGRESS_COLOR = Color.parseColor("#10a679");
private static final int PROGRESS_WIDTH = 3;
private static final int RADIUS = 30;
private int mProgressColor = PROGRESS_COLOR;
private int mProgressWidth = dp2px(PROGRESS_WIDTH);
private int mRadius = dp2px(RADIUS);
private Paint progressPaint;
private int rotateDelta = 4;
private int curAngle = 0;
private int minAngle = -90;
private int startAngle = -90;
private int endAngle = 120;
private Path path;
private Status mStatus = Status.Loading;
private float lineValueLeft;//左边对勾
private float lineValueRight;//右边对勾
private float failLineFirst;//叉号
private float failLineSecond;
public MdStyleProgress(Context context) {
this(context,null);
}
public MdStyleProgress(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public MdStyleProgress(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取自定义属性
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MdStyleProgress);
int indexCount = typedArray.getIndexCount();
for (int i=0;i<indexCount;i++){
int attr = typedArray.getIndex(i);
switch (attr){
case R.styleable.MdStyleProgress_progress_color:
mProgressColor = typedArray.getColor(attr,PROGRESS_COLOR);
break;
case R.styleable.MdStyleProgress_progress_width:
mProgressWidth = (int) typedArray.getDimension(attr,mProgressWidth);
break;
case R.styleable.MdStyleProgress_radius:
mRadius = (int) typedArray.getDimension(attr,mRadius);
break;
}
}
//回收TypedArray对象
typedArray.recycle();
//设置画笔
setPaint();
path = new Path();
path.moveTo(mRadius/2,mRadius);
path.lineTo(mRadius,mRadius+mRadius/2);
path.lineTo(mRadius+mRadius/2,mRadius/2);
}
private void setPaint() {
progressPaint = new Paint();
progressPaint.setAntiAlias(true);
progressPaint.setDither(true);
progressPaint.setColor(mProgressColor);
progressPaint.setStyle(Paint.Style.STROKE);
progressPaint.setStrokeWidth(mProgressWidth);
progressPaint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize;
int heightSize;
if(widthMode != MeasureSpec.EXACTLY){
widthSize = getPaddingLeft() + mProgressWidth + mRadius*2 + getPaddingRight();
widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize,MeasureSpec.EXACTLY);
}
if(heightMode != MeasureSpec.EXACTLY){
heightSize = getPaddingTop() + mProgressWidth + mRadius*2 + getPaddingBottom();
heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize,MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(getPaddingLeft(),getPaddingTop());
if(mStatus == Status.Loading){
if (startAngle == minAngle) {
endAngle += 6;
}
if (endAngle >= 300 || startAngle > minAngle) {
startAngle += 6;
if(endAngle > 20) {
endAngle -= 6;
}
}
if (startAngle > minAngle + 300) {
minAngle = startAngle;
endAngle = 20;
}
canvas.rotate(curAngle += rotateDelta,mRadius,mRadius);//旋转rotateDelta=4的弧长
canvas.drawArc(new RectF(0,0,mRadius*2,mRadius*2),startAngle,endAngle,false,progressPaint);
invalidate();
}else if(mStatus == Status.LoadSuccess){
canvas.drawArc(new RectF(0,0,mRadius*2,mRadius*2),startAngle,360,false,progressPaint);
//canvas.drawPath(path,progressPaint);
//画圆圈中的对勾
canvas.drawLine(mRadius/2,mRadius,mRadius/2+lineValueLeft,mRadius+lineValueLeft,progressPaint);
canvas.drawLine(mRadius,mRadius+mRadius/2,mRadius+lineValueRight,mRadius+mRadius/2-1.5f*lineValueRight,progressPaint);
}else {
canvas.drawArc(new RectF(0,0,mRadius*2,mRadius*2),startAngle,360,false,progressPaint);
//画圆圈中的叉号(画右边叉号的时候,终止位置的x坐标为起始x位置减去failLineFirst,而failLineFirst在属性动画中的取值范围是0-mRadius)
canvas.drawLine(mRadius+mRadius/2,mRadius/2,mRadius*3/2-failLineFirst,mRadius/2+failLineFirst,progressPaint);
canvas.drawLine(mRadius/2,mRadius/2,mRadius/2+failLineSecond,mRadius/2+failLineSecond,progressPaint);
}
canvas.restore();
}
public enum Status{
Loading,
LoadSuccess,
LoadFail
}
public void startAnima(){
//对勾左边线的属性动画
ValueAnimator animatorLeft = ValueAnimator.ofFloat(0f,mRadius/2f);
animatorLeft.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
lineValueLeft = (float) animation.getAnimatedValue();
Log.i("lineValueLeft",lineValueLeft+"");
invalidate();//重绘,调onDraw()重绘
}
});
//对勾右边线的属性动画
ValueAnimator animatorRight = ValueAnimator.ofFloat(0f,mRadius/2f);
animatorRight.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
lineValueRight = (float) animation.getAnimatedValue();
Log.i("lineValueRight",lineValueRight+"");
invalidate();
}
});
//将多个动画组合到一起需要借助AnimatorSet这个类
final AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(animatorRight).after(animatorLeft);
animatorSet.setDuration(150);
animatorSet.start();
}
public void failAnima(){
ValueAnimator failOne = ValueAnimator.ofFloat(0f,mRadius);
failOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
failLineFirst = (float) animation.getAnimatedValue();
invalidate();
}
});
ValueAnimator failOther = ValueAnimator.ofFloat(0f,mRadius);
failOther.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
failLineSecond = (float) animation.getAnimatedValue();
invalidate();
}
});
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(failOther).after(failOne);
animatorSet.setDuration(150);
animatorSet.start();
}
public Status getStatus() {
return mStatus;
}
public void setStatus(Status mStatus) {
this.mStatus = mStatus;
invalidate();
}
/**
* dp 2 px
*/
protected int dp2px(int dpVal)
{
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, getResources().getDisplayMetrics());
}
/**
* sp 2 px
*/
protected int sp2px(int spVal)
{
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, getResources().getDisplayMetrics());
}
}
<com.as.demo_ok75.MdStyleProgress
android:id="@+id/msp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:progress_color="@color/colorPrimary"
app:progress_width="2dp"
app:radius="40dp" />
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final MdStyleProgress msp = findViewById(R.id.msp);
ThreadUtils.executeBySingle(new ThreadUtils.SimpleTask<Object>() {
@Override
public Object doInBackground() throws Throwable {
Thread.sleep(5000);
return null;
}
@Override
public void onSuccess(Object result) {
msp.setStatus(MdStyleProgress.Status.LoadFail);
msp.failAnima();
// msp.setStatus(MdStyleProgress.Status.LoadSuccess);
// msp.startAnima();
}
});
}
}