先看一下效果图
主要是通过贝塞尔曲线去控制绳子的绘制,就直接上代码了
package yuekong.com.progressview;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.animation.DecelerateInterpolator;
/**
* Created by Zhongqi.Shao on 2017/1/17.
*/
public class ProgressView extends SurfaceView implements SurfaceHolder.Callback {
private final int STATE_DOWN = 1;
private final int STATE_UP = 2;
private Context mContext;
private Path mPath;
//中间绳子的Paint
private Paint mPaint;
private Paint mCirclePaint;
//向下运动
private ValueAnimator mDownAnimator;
//向上运动
private ValueAnimator mUpAnimator;
//从上至下 自由落体运动
private ValueAnimator mFreeAnimator;
private AnimatorSet mAnimationSet;
private boolean isAnimationShowing = false;
//中间绳子的高度
private float mLineHeigth;
//中间绳子的宽度
private float mLineWidth;
//向下运动的距离
private float mDownDistance = 0f;
//向上运动的距离
private float mUpDistance = 0f;
//自由落体运动的距离
private float mFreeDistance = 0f;
private int mState = STATE_DOWN;
private boolean mIsBounced = false;
public ProgressView(Context context) {
this(context, null);
mContext = context;
}
public ProgressView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
mContext = context;
}
public ProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
init();
}
private void init() {
mLineHeigth = dp2px(3);
mLineWidth = dp2px(100);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mLineHeigth);
//圆形线帽
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setColor(getContext().getResources().getColor(R.color.auto_yellow));
mCirclePaint = new Paint();
mCirclePaint.setAntiAlias(true);
mCirclePaint.setColor(mContext.getResources().getColor(R.color.black));
mCirclePaint.setStyle(Paint.Style.FILL);
mPath = new Path();
SurfaceHolder holder = getHolder();
holder.addCallback(this);
initAnimator();
}
private void initAnimator() {
mDownAnimator = ValueAnimator.ofFloat(0, 1);
mDownAnimator.setDuration(500);
mDownAnimator.setInterpolator(new DecelerateInterpolator());
mDownAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mDownDistance = (float) valueAnimator.getAnimatedValue() * 50;
postInvalidate();
}
});
mDownAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
mState = STATE_DOWN;
}
@Override
public void onAnimationEnd(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
mUpAnimator = ValueAnimator.ofFloat(0, 1);
mUpAnimator.setDuration(900);
mUpAnimator.setInterpolator(new DecelerateInterpolator());
mUpAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mUpDistance = (float) valueAnimator.getAnimatedValue() * 50;
if (mUpDistance >= 50) {
//进入自由落体
mIsBounced = true;
if (!mFreeAnimator.isRunning()) {
mFreeAnimator.start();
}
}
postInvalidate();
}
});
mUpAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
mState = STATE_UP;
}
@Override
public void onAnimationEnd(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
mFreeAnimator = ValueAnimator.ofFloat(0, 6.8f);
mFreeAnimator.setDuration(900);
mFreeAnimator.setInterpolator(new DecelerateInterpolator());
mFreeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float t = (float) valueAnimator.getAnimatedValue();
mFreeDistance = 34 * t - 5 * t * t;
postInvalidate();
}
});
mFreeAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
mIsBounced = true;
}
@Override
public void onAnimationEnd(Animator animator) {
isAnimationShowing = false;
//执行第二次动画
startAllAnimator();
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
mAnimationSet = new AnimatorSet();
mAnimationSet.play(mDownAnimator).before(mUpAnimator);
mAnimationSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
isAnimationShowing = true;
}
@Override
public void onAnimationEnd(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
}
public void startAllAnimator() {
if (isAnimationShowing) {
return;
}
if (mAnimationSet.isRunning()) {
mAnimationSet.end();
mAnimationSet.cancel();
}
mIsBounced = false;
mAnimationSet.start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/*
* 中间绳子分成左右两个二阶贝塞尔曲线绘制
* */
mPath.reset();
mPath.moveTo(getWidth() / 2 - mLineWidth / 2, getHeight() / 2);
if (mState == STATE_DOWN) {
//向下运动
//左边的贝塞尔
mPath.quadTo((float) (getWidth() / 2 - mLineWidth / 2 + mLineWidth * 0.375), getHeight() / 2 + mDownDistance, getWidth() / 2, getHeight() / 2 + mDownDistance);
//右边的贝塞尔
mPath.quadTo((float) (getWidth() / 2 + mLineWidth / 2 - mLineWidth * 0.375), getHeight() / 2 + mDownDistance, getWidth() / 2 + mLineWidth / 2, getHeight() / 2);
canvas.drawPath(mPath, mPaint);
//绘制小球
canvas.drawCircle(getWidth() / 2, getHeight() / 2 + mDownDistance - 10, 10, mCirclePaint);
} else if (mState == STATE_UP) {
//向上运动
//左边的贝塞尔
mPath.quadTo((float) (getWidth() / 2 - mLineWidth / 2 + mLineWidth * 0.375), getHeight() / 2 + (50 - mUpDistance), getWidth() / 2, getHeight() / 2 + (50 - mUpDistance));
//右边的贝塞尔
mPath.quadTo((float) (getWidth() / 2 + mLineWidth / 2 - mLineWidth * 0.375), getHeight() / 2 + (50 - mUpDistance), getWidth() / 2 + mLineWidth / 2, getHeight() / 2);
canvas.drawPath(mPath, mPaint);
//第三种状态 最大运动长度设置为50 半径为10
if (!mIsBounced) {
//正常向上运动
canvas.drawCircle(getWidth() / 2, getHeight() / 2 + (50 - mUpDistance) - 10, 10, mCirclePaint);
} else {
//自由落体状态
canvas.drawCircle(getWidth() / 2, getHeight() / 2 - mFreeDistance - 10, 10, mCirclePaint);
}
}
//绘制两边固定的小圆
//两边小球的颜色
canvas.drawCircle(getWidth() / 2 - mLineWidth / 2, getHeight() / 2, 10, mCirclePaint);
canvas.drawCircle(getWidth() / 2 + mLineWidth / 2, getHeight() / 2, 10, mCirclePaint);
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
//锁住画布
Canvas canvas = surfaceHolder.lockCanvas();
//开始绘制
draw(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
private int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
private int sp2px(int sp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
}
}