Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android跳動的小球動畫

android跳動的小球動畫

編輯:關於Android編程


平時對牛逼動畫,高級UI,都深入的不多!近日,某頭條,推了一個android技術類視頻(平時在頭條關注技術比較多),講的是加載動畫效果,是動腦學院講的公開課,160分鐘,我硬是拿著小手機看完了!邊看,便記,然後整理了一下,規范了一下代碼
做這個動畫,需掌握:
1、屬性動畫
2、Path類、Canvas類
3、貝塞爾曲線
4、SurfaceView用法
5、自定義attr屬性
6 、架構: 狀態模式,控制器
7 、自由落體,拋物線等概念
不多說了,直接上碼

1.DancingView.java

public class DancingView extends SurfaceView implements SurfaceHolder.Callback {

    public static final int STATE_DOWN = 1;//向下狀態
    public static final int STATE_UP = 2;//向上狀態

    public static final int DEFAULT_POINT_RADIUS = 10;
    public static final int DEFAULT_BALL_RADIUS = 13;
    public static final int DEFAULT_LINE_WIDTH = 200;
    public static final int DEFAULT_LINE_HEIGHT = 2;
    public static final int DEFAULT_LINE_COLOR = Color.parseColor("#FF9800");
    public static final int DEFAULT_POINT_COLOR = Color.parseColor("#9C27B0");
    public static final int DEFAULT_BALL_COLOR = Color.parseColor("#FF4081");

    public static final int DEFAULT_DOWN_DURATION = 600;//ms
    public static final int DEFAULT_UP_DURATION = 600;//ms
    public static final int DEFAULT_FREEDOWN_DURATION = 1000;//ms

    public static final int MAX_OFFSET_Y = 50;//水平下降最大偏移距離


    public int PONIT_RADIUS = DEFAULT_POINT_RADIUS;//小球半徑
    public int BALL_RADIUS = DEFAULT_BALL_RADIUS;//小球半徑

    private Paint mPaint;
    private Path mPath;
    private int mLineColor;
    private int mPonitColor;
    private int mBallColor;
    private int mLineWidth;
    private int mLineHeight;

    private float mDownDistance;
    private float mUpDistance;
    private float freeBallDistance;

    private ValueAnimator mDownController;//下落控制器
    private ValueAnimator mUpController;//上彈控制器
    private ValueAnimator mFreeDownController;//自由落體控制器

    private AnimatorSet animatorSet;
    private int state;

    private boolean ismUpControllerDied = false;
    private boolean isAnimationShowing = false;
    private boolean isBounced = false;
    private boolean isBallFreeUp = false;

    public DancingView(Context context) {
        super(context);
        init(context, null);
    }

    public DancingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }


    public DancingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }


    private void init(Context context, AttributeSet attrs) {
        initAttributes(context, attrs);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(mLineHeight);
        mPaint.setStrokeCap(Paint.Cap.ROUND);

        mPath = new Path();
        getHolder().addCallback(this);

        initController();
    }

    private void initAttributes(Context context, AttributeSet attrs) {
        TypedArray typeArray = context.obtainStyledAttributes(attrs, R.styleable.DancingView);
        mLineColor = typeArray.getColor(R.styleable.DancingView_lineColor, DEFAULT_LINE_COLOR);
        mLineWidth = typeArray.getDimensionPixelOffset(R.styleable.DancingView_lineWidth, DEFAULT_LINE_WIDTH);
        mLineHeight = typeArray.getDimensionPixelOffset(R.styleable.DancingView_lineHeight, DEFAULT_LINE_HEIGHT);
        mPonitColor = typeArray.getColor(R.styleable.DancingView_pointColor, DEFAULT_POINT_COLOR);
        mBallColor = typeArray.getColor(R.styleable.DancingView_ballColor, DEFAULT_BALL_COLOR);
        typeArray.recycle();
    }


    private void initController() {
        mDownController = ValueAnimator.ofFloat(0, 1);
        mDownController.setDuration(DEFAULT_DOWN_DURATION);
        mDownController.setInterpolator(new DecelerateInterpolator());
        mDownController.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mDownDistance = MAX_OFFSET_Y * (float) animation.getAnimatedValue();
                postInvalidate();
            }
        });
        mDownController.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                state = STATE_DOWN;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
            }

            @Override
            public void onAnimationCancel(Animator animation) {
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
            }
        });

        mUpController = ValueAnimator.ofFloat(0, 1);
        mUpController.setDuration(DEFAULT_UP_DURATION);
        mUpController.setInterpolator(new DancingInterpolator());
        mUpController.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mUpDistance = MAX_OFFSET_Y * (float) animation.getAnimatedValue();
                if (mUpDistance >= MAX_OFFSET_Y) {
                    //進入自由落體狀態
                    isBounced = true;
                    if (!mFreeDownController.isRunning() && !mFreeDownController.isStarted() && !isBallFreeUp) {
                        mFreeDownController.start();
                    }
                }
                postInvalidate();
            }
        });
        mUpController.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                state = STATE_UP;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                ismUpControllerDied = true;
            }

            @Override
            public void onAnimationCancel(Animator animation) {
            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });

        mFreeDownController = ValueAnimator.ofFloat(0, 8f);
        mFreeDownController.setDuration(DEFAULT_FREEDOWN_DURATION);
        mFreeDownController.setInterpolator(new DecelerateInterpolator());
        mFreeDownController.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //該公式解決上升減速 和 下降加速
                float t = (float) animation.getAnimatedValue();
                freeBallDistance = 40 * t - 5 * t * t;

                if (ismUpControllerDied) {//往上拋,到臨界點
                    postInvalidate();
                }
            }
        });
        mFreeDownController.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                isBallFreeUp = true;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                isAnimationShowing = false;
                //循環第二次
                startAnimations();
            }

            @Override
            public void onAnimationCancel(Animator animation) {
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
            }
        });

        animatorSet = new AnimatorSet();
        animatorSet.play(mDownController).before(mUpController);
        animatorSet.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                isAnimationShowing = true;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
            }

            @Override
            public void onAnimationCancel(Animator animation) {
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
            }
        });
    }

    /**
     * 啟動動畫,外部調用
     */
    public void startAnimations() {
        if (isAnimationShowing) {
            return;
        }
        if (animatorSet.isRunning()) {
            animatorSet.end();
            animatorSet.cancel();
        }
        isBounced = false;
        isBallFreeUp = false;
        ismUpControllerDied = false;

        animatorSet.start();
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);


        // 一條繩子用左右兩部分的二階貝塞爾曲線組成
        mPaint.setColor(mLineColor);
        mPath.reset();
        //起始點
        mPath.moveTo(getWidth() / 2 - mLineWidth / 2, getHeight() / 2);

        if (state == 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);

            mPaint.setStyle(Paint.Style.STROKE);
            canvas.drawPath(mPath, mPaint);
            /**************繪制繩子結束*************/


            /**************繪制彈跳小球開始*************/
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setColor(mBallColor);
            canvas.drawCircle(getWidth() / 2, getHeight() / 2 + mDownDistance - BALL_RADIUS, BALL_RADIUS, mPaint);
            /**************繪制彈跳小球結束*************/
        } else if (state == 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);

            mPaint.setStyle(Paint.Style.STROKE);
            canvas.drawPath(mPath, mPaint);
            /**************繪制繩子結束*************/

            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setColor(mBallColor);

            //彈性小球,自由落體
            if (!isBounced) {
                //上升
                canvas.drawCircle(getWidth() / 2, getHeight() / 2 + (MAX_OFFSET_Y - mUpDistance) - BALL_RADIUS, BALL_RADIUS, mPaint);
            } else {
                //自由落體
                canvas.drawCircle(getWidth() / 2, getHeight() / 2 - freeBallDistance - BALL_RADIUS, BALL_RADIUS, mPaint);
            }
        }
        mPaint.setColor(mPonitColor);
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(getWidth() / 2 - mLineWidth / 2, getHeight() / 2, PONIT_RADIUS, mPaint);
        canvas.drawCircle(getWidth() / 2 + mLineWidth / 2, getHeight() / 2, PONIT_RADIUS, mPaint);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Canvas canvas = holder.lockCanvas();//鎖定整個SurfaceView對象,獲取該Surface上的Canvas.
        draw(canvas);
        holder.unlockCanvasAndPost(canvas);//釋放畫布,提交修改
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
    }
}

2.DancingInterpolator.java

 public class DancingInterpolator implements Interpolator {
    @Override
    public float getInterpolation(float input) {
        return (float) (1 - Math.exp(-3 * input) * Math.cos(10 * input));
    }
}

3.自定義屬性 styles.xml


       
       
       
       
       
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved