先看一下效果圖
主要是通過貝塞爾曲線去控制繩子的繪製,就直接上代碼了
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());
}
}