Android自定義View貝塞爾曲線最佳實現

先看一下效果圖


主要是通過貝塞爾曲線去控制繩子的繪製,就直接上代碼了


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());
    }
}





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章