小球滑动已八卦方式滑进滑出、可以在外圈滑动的BallSpinningView

今天又个下拉刷新的需求,UI需要我们自定义一个比较刷新动画。两个重叠小球先按太极分割线的方式到圆的两端,然后进行旋转,最后又已太极的形式收回来。
说着很简单,但是做起来还是有一点难度的,需要计算蛮多东西的,对于我这种数学不好得人很要命啊。
先说思路吧

  1. 先在控件上绘制重叠的两个圆
  2. 开启滑动之后,两个小圆按八卦的形式滑动出去,此时他们的轨迹是一个半圆形,直径是外层轨迹(小圆将在他的圆线上滑动)的半径,可以先确定两个半圆的圆心,根据公式计算出八卦分割线上每个点的的坐标
  3. 当两个小圆滑动到大圆上时,进行旋转
  4. 回收,思路与2一样
//根据圆心、半径求圆上每一个点的坐标
X坐标=a + Math.sin(2*Math.PI / 360*角度) * r ;
Y坐标=b + Math.cos(2*Math.PI / 360*角度) * r ;

上核心代码

  /**
     * 定义小球的状态
     * 0、初始化化阶段
     * 1、两个小球向沿八卦从圆心滑动到圆上
     * 2、两个小球进行旋转
     * 3、小球沿八卦从圆上滑动到圆心
     * 4、小球交换位置后沿八卦从圆心滑动到圆上
     * 5、两个小球进行旋转
     * 6、小球交换位置小球沿八卦从圆上滑动到圆心
     */
    private int mState = 0;
      //小圆的半径
    private int mRadius;
    //大圆的半径(轨迹)
    private int mMaxRadius = 150;
    //小圆在轨迹上快速滑动的速度
    private int mFast = 8;
    //小圆在轨迹上快速滑动的速度(角度)
    private int mSlow = 6;
    //小圆缩放时的速度(角度)
    private int mZoom = 10;
    //上半部分的圆上的点以及角度(角度)
    private int mTopPointX;
    //小圆滑动的距离(角度)
    private int mDistance = 360;

    private int mTopPointY;
    private int mTopAngle = 0; //默认为0
    //下半部分的圆上的点以及角度
    private int mBottomPointX;
    private int mBottomPointY;
    private int mBottomAngle = 180; //默认为180
    //小圆绕大圆滑动距离
    private int mRotateAngle = 0;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mState == 0) /*初始状态*/ {
            stateFor0(canvas);
        }
        if (mState == 1)/*小球沿八卦从圆心滑动到圆上*/ {
            stateFor1(canvas);
        }
        if (mState == 2)/*小球在圆上滑动*/ {
            stateFor2(canvas);
        }
        if (mState == 3)/*小球沿八卦从圆上滑动到圆心*/ {
            stateFor3(canvas);
        }
        if (mState == 4)/*两个小球交换位置后小球沿八卦从圆心滑动到圆上*/ {
            stateFor4(canvas);
        }
        if (mState == 5)/*小球沿八卦在圆上滑动*/ {
            stateFor5(canvas);
        }
        if (mState == 6)/*两个小球交换位置后小球沿八卦从圆上滑动到圆心*/ {
            stateFor6(canvas);
        }
    }

    private void stateFor0(Canvas canvas) {
        for (Paint paint : mPaints) {
            canvas.drawCircle(mMeasuredWidth / 2, mMeasuredHeight / 2, mRadius, paint);
            canvas.save();
        }
    }

    private void stateFor1(Canvas canvas) {
        for (int i = 0; i < mPaints.size(); i++) {
            if (i == 0) {
                mTopPointX = (int) (mMeasuredWidth / 2 + Math.sin(2 * Math.PI / 360 * mTopAngle) * mMaxRadius);
                mTopPointY = (int) (mMeasuredHeight / 2 - mMaxRadius + Math.cos(2 * Math.PI / 360 * mTopAngle) * mMaxRadius);
                canvas.drawCircle(mTopPointX, mTopPointY, mRadius, mPaints.get(i));
            } else {
                mBottomPointX = (int) (mMeasuredWidth / 2 + Math.sin(2 * Math.PI / 360 * mBottomAngle) * mMaxRadius);
                mBottomPointY = (int) (mMeasuredHeight / 2 + mMaxRadius + Math.cos(2 * Math.PI / 360 * mBottomAngle) * mMaxRadius);
                canvas.drawCircle(mBottomPointX, mBottomPointY, mRadius, mPaints.get(i));
            }
        }
        if (mBottomAngle == 0) {
            mState = 2;
            //重置数据,为进入mState=3状态做准备
            mBottomAngle = 0;
            mTopAngle = 180;
            invalidate();
            return;
        }
        mTopAngle -= mZoom;
        mBottomAngle -= mZoom;
        invalidate();
    }

    private void stateFor2(Canvas canvas) {
        for (int i = 0; i < mPaints.size(); i++) {
            if (i == 0) {
                canvas.drawCircle(mMeasuredWidth / 2, mMeasuredHeight / 2 - mMaxRadius * 2, mRadius, mPaints.get(i));
            } else {
                canvas.drawCircle(mMeasuredWidth / 2, mMeasuredHeight / 2 + mMaxRadius * 2, mRadius, mPaints.get(i));
            }
        }
        if (mRotateAngle % 360 <= 60)/*慢速旋转*/ {
            mRotateAngle += mSlow;
        } else if (mRotateAngle % 360 <= 300)/*快速旋转*/ {
            mRotateAngle += mFast;
        } else if (mRotateAngle % 360 < 360)/*慢速旋转*/ {
            mRotateAngle += mSlow;
            if (mRotateAngle > mDistance) { //必须判断,防止角度出问题
                mRotateAngle = mDistance;
            }
        }
        setRotation(mRotateAngle);
        if (mRotateAngle >= mDistance) {
            mRotateAngle = 0;
            mState = 3;
            invalidate();
            return;
        }
        invalidate();
    }

    private void stateFor3(Canvas canvas) {
        for (int i = 0; i < mPaints.size(); i++) {
            if (i == 0) {
                mTopPointX = (int) (mMeasuredWidth / 2 + Math.sin(2 * Math.PI / 360 * mTopAngle) * mMaxRadius);
                mTopPointY = (int) (mMeasuredHeight / 2 - mMaxRadius + Math.cos(2 * Math.PI / 360 * mTopAngle) * mMaxRadius);
                canvas.drawCircle(mTopPointX, mTopPointY, mRadius, mPaints.get(i));
            } else {
                mBottomPointX = (int) (mMeasuredWidth / 2 + Math.sin(2 * Math.PI / 360 * mBottomAngle) * mMaxRadius);
                mBottomPointY = (int) (mMeasuredHeight / 2 + mMaxRadius + Math.cos(2 * Math.PI / 360 * mBottomAngle) * mMaxRadius);
                canvas.drawCircle(mBottomPointX, mBottomPointY, mRadius, mPaints.get(i));
            }
        }
        if (mTopAngle == 0) {
            postDelayed(new Runnable() {
                @Override
                public void run() {
                    mState = 4;
                    invalidate();
                }
            }, 80);
            //重置数据,为mState = 1状态做准备
            mBottomAngle = 180;
            mTopAngle = 0;
            invalidate();
            return;
        }
        mTopAngle -= mZoom;
        mBottomAngle -= mZoom;
        invalidate();
    }

    private void stateFor4(Canvas canvas) {
        for (int i = 0; i < mPaints.size(); i++) {
            if (i == 1) {
                mTopPointX = (int) (mMeasuredWidth / 2 + Math.sin(2 * Math.PI / 360 * mTopAngle) * mMaxRadius);
                mTopPointY = (int) (mMeasuredHeight / 2 - mMaxRadius + Math.cos(2 * Math.PI / 360 * mTopAngle) * mMaxRadius);
                canvas.drawCircle(mTopPointX, mTopPointY, mRadius, mPaints.get(i));
            } else {
                mBottomPointX = (int) (mMeasuredWidth / 2 + Math.sin(2 * Math.PI / 360 * mBottomAngle) * mMaxRadius);
                mBottomPointY = (int) (mMeasuredHeight / 2 + mMaxRadius + Math.cos(2 * Math.PI / 360 * mBottomAngle) * mMaxRadius);
                canvas.drawCircle(mBottomPointX, mBottomPointY, mRadius, mPaints.get(i));
            }
        }
        if (mBottomAngle == 0) {
            mState = 5;
            //重置数据,为进入mState=3状态做准备
            mBottomAngle = 0;
            mTopAngle = 180;
            invalidate();
            return;
        }
        mTopAngle -= mZoom;
        mBottomAngle -= mZoom;
        invalidate();
    }

    private void stateFor5(Canvas canvas) {
        for (int i = 0; i < mPaints.size(); i++) {
            if (i == 1) {
                canvas.drawCircle(mMeasuredWidth / 2, mMeasuredHeight / 2 - mMaxRadius * 2, mRadius, mPaints.get(i));
            } else {
                canvas.drawCircle(mMeasuredWidth / 2, mMeasuredHeight / 2 + mMaxRadius * 2, mRadius, mPaints.get(i));
            }
        }
        if (mRotateAngle % 360 <= 60)/*慢速旋转*/ {
            mRotateAngle += mSlow;
        } else if (mRotateAngle % 360 <= 300)/*快速旋转*/ {
            mRotateAngle += mFast;
        } else if (mRotateAngle % 360 < 360)/*慢速旋转*/ {
            mRotateAngle += mSlow;
            if (mRotateAngle > mDistance) { //必须判断,防止角度出问题
                mRotateAngle = mDistance;
            }
        }
        setRotation(mRotateAngle);
        if (mRotateAngle >= mDistance) {
            mRotateAngle = 0;
            mState = 6;
            invalidate();
            return;
        }
        invalidate();
    }

    private void stateFor6(Canvas canvas) {
        for (int i = 0; i < mPaints.size(); i++) {
            if (i == 1) {
                mTopPointX = (int) (mMeasuredWidth / 2 + Math.sin(2 * Math.PI / 360 * mTopAngle) * mMaxRadius);
                mTopPointY = (int) (mMeasuredHeight / 2 - mMaxRadius + Math.cos(2 * Math.PI / 360 * mTopAngle) * mMaxRadius);
                canvas.drawCircle(mTopPointX, mTopPointY, mRadius, mPaints.get(i));
            } else {
                mBottomPointX = (int) (mMeasuredWidth / 2 + Math.sin(2 * Math.PI / 360 * mBottomAngle) * mMaxRadius);
                mBottomPointY = (int) (mMeasuredHeight / 2 + mMaxRadius + Math.cos(2 * Math.PI / 360 * mBottomAngle) * mMaxRadius);
                canvas.drawCircle(mBottomPointX, mBottomPointY, mRadius, mPaints.get(i));
            }
        }
        if (mTopAngle == 0) {
            postDelayed(new Runnable() {
                @Override
                public void run() {
                    mState = 1;
                    invalidate();
                }
            }, 80);
            //重置数据,为mState = 1状态做准备
            mBottomAngle = 180;
            mTopAngle = 0;
            invalidate();
            return;
        }
        mTopAngle -= mZoom;
        mBottomAngle -= mZoom;
        invalidate();
    }

    /**
     * 开始旋转(最后一个调用的方法,开始之后不能改变属性)
     */
    public void startSpinning() {
        mState = 1;
        invalidate();
    }

这样,调用startSpinning()就可以运行了。还可以提供一些设置方法。这个控件就完成了

写在结尾:我感觉这个控件写的有点复杂了,应该有更简单的方式,希望哪位看到的话可以帮我修改下,让我可以学到更好的解决方式

控件项目地址:https://git.oschina.net/qiangshen/commentview.git
这是我开始维护的一个控件地址,这里面的控件都是我自己学习时或工作时写的,可能控件还有各种bug,可能我的做法太复杂或代码规范有问题,我希望大家可以帮我提出并改正。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值