为了账号安全,请及时绑定邮箱和手机立即绑定

阳光加载小动画

标签:
Android

在逛UI中国时,看到一个阳光加载小动画,觉得好喜欢,于是就也想实现一下,并在我的App中使用:5bdf101b0001c07400350017.jpg


于是就在想有什么方法可以画出螺旋线,起初我想通过螺旋线的参数方程进行实现,无奈在网上搜来搜去也只找到它的极坐标方程,且不容易转换成直角坐标方程,后来我又想到一个方法,就是通过drawArc()方法画若干个90°的圆弧进行实现,具体思路如图

5bdf101f00018cd002440325.jpg

后来发现这样子画出的螺旋线特别别扭,而且不知道在没去实现这样一个动画,就又想啦另外一种实现的思路,通过ObjectAnimator来进行实现,具体思路如下:

5bdf102000016b5502440325.jpg

即通过ObjectAnimator同时扩大圆弧的起始角度和半径,这样子又可以实现动画,切画出的螺旋线也比较圆滑,之后即通过一定的三角函数来控制那5点阳光的位置,并把它画出来,大致的实现效果为(gif图制作有点粗糙)

5bdf102000018ae402250402.jpg

具体代码如下:
自定View
SunnyLoad.java

public class SunnyLoad extends View {
    //View's width
    private int VIEW_WIDTH;
    //View's height
    private int VIEW_HEIGHT;
    //画笔
    private Paint mPaint;

    //圆弧的半径
    private float mRadius;

    //圆弧的起始角度
    private float mStartAngel = 0f;

    //缓存位图
    private Bitmap mCacheBitmap;

    //缓存画布
    private Canvas mCacheCanvas;

    //是否为第一个动画
    private boolean isFirstAnim = true;

    //最大半径
    private float maxRadius = -1;

    //最小半径
    private float minRadius = -1;

    //光线长度
    private float mLineLength;

    //光线最大长度
    private float maxLineLength = -1;

    //一次完整动画是否结束的标志
    private boolean isFinished = false;

    private RectF mRectF;

    private int index = 0;

    private static final int INDEX_ONE = 0;
    private static final int INDEX_TWO = 1;
    private static final int INDEX_THREE = 2;
    private static final int INDEX_FOUR = 3;
    private static final int INDEX_FIVE = 4;
    private static final int[] INDEXES = new int[]{INDEX_ONE, INDEX_TWO, INDEX_THREE, INDEX_FOUR, INDEX_FIVE};

    public SunnyLoad(Context context) {
        super(context);
        init();
    }

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

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

    private void init() {
        mRectF = new RectF();
        mPaint = new Paint();
        mPaint.setColor(Color.YELLOW);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeCap(Paint.Cap.ROUND);

        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(30f);

        postDelayed(new Runnable() {
            @Override
            public void run() {
                startFirstAnim(0);
            }
        },1000);

        invalidate();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        VIEW_WIDTH = getWidth();
        VIEW_HEIGHT = getHeight();
        mCacheBitmap = Bitmap.createBitmap(VIEW_WIDTH, VIEW_HEIGHT, Bitmap.Config.ARGB_8888);
        mCacheCanvas = new Canvas(mCacheBitmap);
    }

    /**
     * 设置画笔的颜色和粗细
     *
     * @param color 画笔颜色
     * @param width 画笔粗细
     */
    public void setColorAndPaintWidth(int color, float width) {
        mPaint.setColor(color);
        mPaint.setStrokeWidth(width);

        invalidate();
    }

    private void startFirstAnim(long delayed) {
        if (maxRadius == -1) maxRadius = this.VIEW_WIDTH * 0.1492f;
        if (minRadius == -1) minRadius = this.VIEW_WIDTH * 0.0333f;
        if (maxLineLength == -1) maxLineLength = this.maxRadius * 0.3333f;

//        System.out.println("FirstAnim");

        AnimatorSet animatorSet = new AnimatorSet();
        ObjectAnimator angel_animator = ObjectAnimator.ofFloat(this, "startAngel", -60, 450);
        ObjectAnimator radius_animator = ObjectAnimator.ofFloat(this, "radius", minRadius, maxRadius);
        animatorSet.playTogether(angel_animator, radius_animator);
        animatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                isFirstAnim=false;
                index = INDEX_ONE;
                startSecondAnim();
                super.onAnimationEnd(animation);
            }
        });
        animatorSet.setDuration(2500);
        animatorSet.setStartDelay(delayed);
        animatorSet.start();

    }

    private void startSecondAnim() {
//        System.out.println("SecondAnim");
        ObjectAnimator length_animator = ObjectAnimator.ofFloat(this, "lineLength", 0, maxLineLength);
        length_animator.setDuration(200);
        length_animator.setInterpolator(new AccelerateInterpolator());
        length_animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                if (index != INDEXES.length) {
                    index++;
                    startSecondAnim();
                } else {
                    isFirstAnim=true;
                    isFinished =true;
                }
            }
        });
        length_animator.start();
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        if (isFinished) {
            //清除缓存
            mCacheCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
            startFirstAnim(1000);
            isFinished=false;
            return;
        }

        if (isFirstAnim) {
            mRectF.left = VIEW_WIDTH * 0.4854f - mRadius;
            mRectF.right = VIEW_WIDTH * 0.4854f + mRadius;
            mRectF.top = VIEW_HEIGHT * 0.5f - mRadius;
            mRectF.bottom = VIEW_HEIGHT * 0.5f + mRadius;

            mCacheCanvas.drawArc(mRectF, mStartAngel, 20, false, mPaint);
            canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint);
        }
        if (!isFirstAnim) {
            switch (index) {
                case INDEX_ONE:
                    mCacheCanvas.drawLine(VIEW_WIDTH * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.9397f, VIEW_HEIGHT * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.3420f, VIEW_WIDTH * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.9397f, VIEW_HEIGHT * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.3420f, mPaint);
                    canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint);
                    break;
                case INDEX_TWO:
                    mCacheCanvas.drawLine(VIEW_WIDTH * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.5000f, VIEW_HEIGHT * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.8660f, VIEW_WIDTH * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.5000f, VIEW_HEIGHT * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.8660f, mPaint);
                    canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint);
                    break;
                case INDEX_THREE:
                    mCacheCanvas.drawLine(VIEW_WIDTH * 0.5f + (maxRadius + VIEW_WIDTH * 0.04255f) * 0.1736f, VIEW_HEIGHT * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.9848f, VIEW_WIDTH * 0.5f + ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.1736f, VIEW_HEIGHT * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.9848f, mPaint);
                    canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint);
                    break;
                case INDEX_FOUR:
                    mCacheCanvas.drawLine(VIEW_WIDTH * 0.5f + (maxRadius + VIEW_WIDTH * 0.04255f) * 0.7660f, VIEW_HEIGHT * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.6428f, VIEW_WIDTH * 0.5f + ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.7660f, VIEW_HEIGHT * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.6428f, mPaint);
                    canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint);
                    break;
                case INDEX_FIVE:
                    mCacheCanvas.drawLine(VIEW_WIDTH * 0.5f + (maxRadius + VIEW_WIDTH * 0.04255f), VIEW_HEIGHT * 0.5f, VIEW_WIDTH * 0.5f + ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength), VIEW_HEIGHT * 0.5f, mPaint);
                    canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint);
                    break;
            }

        }

    }

    public void setMaxRaius(float maxRadius) {
        this.maxRadius = maxRadius;
    }

    public float getMaxRaius() {

        return maxRadius;
    }

    public float getStartAngel() {
        return mStartAngel;
    }

    public void setStartAngel(float startAngel) {
        mStartAngel = startAngel;
        invalidate();
    }


    public float getLineLength() {
        return mLineLength;
    }

    public float getMaxLineLength() {
        return maxLineLength;
    }

    public void setMaxLineLength(float maxLineLength) {
        this.maxLineLength = maxLineLength;
    }

    public void setLineLength(float lineLength) {
        mLineLength = lineLength;
        invalidate();
    }

    public float getRadius() {
        return mRadius;
    }

    public void setRadius(float radius) {
        mRadius = radius;
        invalidate();
    }

    public Paint getPaint() {
        return mPaint;
    }

    public void setPaint(Paint paint) {
        mPaint = paint;
    }

    public Bitmap getCacheBitmap() {
        return mCacheBitmap;
    }

    public void setCacheBitmap(Bitmap cacheBitmap) {
        mCacheBitmap = cacheBitmap;
    }

    public Canvas getCacheCanvas() {
        return mCacheCanvas;
    }

    public void setCacheCanvas(Canvas cacheCanvas) {
        mCacheCanvas = cacheCanvas;
    }

    public boolean isFirstAnim() {
        return isFirstAnim;
    }

    private void setIsFirstAnim(boolean isFirstAnim) {
        this.isFirstAnim = isFirstAnim;
    }

    public float getMinRadius() {
        return minRadius;
    }

    public void setMinRadius(float minRadius) {
        this.minRadius = minRadius;
    }

    public float getMaxRadius() {
        return maxRadius;
    }

    public void setMaxRadius(float maxRadius) {
        this.maxRadius = maxRadius;
    }

    public boolean isFinished() {
        return isFinished;
    }

    private void setIsFinished(boolean isFinished) {
        this.isFinished = isFinished;
    }
}



就这样,一个简单的小动画就做好啦,但是,我发现这个动画做啦大量的运算和重绘,性能不怎么好,希望有人可以指导一下我该怎么样做出优秀的动画,而且对于5个光线的伸缩动画我也实现不了,希望有人可以帮助我。
最后,我发现自定义个性能好有漂亮的控件真的很难,而且对数学的要求也很高,我还有很多地方要努力,加油!
完整源码地址:https://github.com/wuapnjie/SunnyLoad

原文链接:http://www.apkbus.com/blog-592372-59473.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消