android大漩涡旋转效果(极光星空)

先放张效果图,gif录制把效果都显示成卡顿样子,其实不然

android大漩涡旋转效果(极光星空)

其实这是项目中的一个需求啦,我想着也写出来自己做个记录。

先来简单看下这个效果

1,背后有一个一直旋转的类似极光星空的效果,每个圆之间的距离逐渐增大,每个圆上有固定的两个黄色弧线(可变的)

2,往看上层view,两个转向不同的花轮,其实这是两个图片,加上一个旋转动画的效果(中间是app的icon)

3,不断的添加图片,做成一个漩涡样式的动画,刚开始做的时候想的是在哪里见过垃圾回收好像是这种动画

先贴下1的代码

   private int defaultRadius = 100;//默认的半径 最开始的一个圆的半径
    private int circleSpace = 10;//每个圆的间隔

private void init() {
        defaultRadius = ScreenUtil.dip2px(mContext, 100);
//        c²=a²+b²
        if (mPaint == null) {

            mScreenHeight = ScreenUtil.getScreenHeight(mContext);
            mScreenWidth = ScreenUtil.getScreenWidth(mContext);
            int cf = mScreenWidth * mScreenWidth + mScreenHeight * mScreenHeight;
            mSqrt = Math.sqrt(cf);
            mCircleCount = (int) Math.abs((mSqrt - defaultRadius * 2) / (circleSpace * 2));


            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setColor(Color.parseColor("#D2D2D2"));
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeWidth(2);
        }

    }

init其实是做了一些初始化和计算的操作,有一个默认的第一圆的变径大小,因为中间有一个icon的存在,就不需要从很小的圆开始画起了,以及每个圆之间固定的间隔,这个间隔后面再讲。

计算的是我这个一个屏幕画多少个圆就足够了?为了避免资源浪费,还是需要想到处理一下的,也比较简单,通过简单的三角函数,求出对角线距离,对角线距离减掉默认圆的直径再除固定间隔,会得到一个我们要画的圆的数量。

@Override
    protected void onDraw(Canvas canvas) {
        defaultRadius = ScreenUtil.dip2px(mContext, 100);
        circleSpace = 10;
        try {
            if (acrList == null || acrList.size() <= 0) {
                saveArc();
                invalidate();
                return;
            }
            for (int i = 0; i < acrList.size(); i++) {
                defaultRadius = defaultRadius + circleSpace + i * 5;
                if (defaultRadius >= mSqrt) {
                    break;
                }
                mPaint.setStrokeWidth(2);
                mPaint.setColor(Color.parseColor("#D2D2D2"));
                canvas.drawCircle(mScreenWidth / 2, mScreenHeight / 2, defaultRadius, mPaint);

                mRectF = new RectF(mScreenWidth / 2 - defaultRadius, mScreenHeight / 2 - defaultRadius, mScreenWidth / 2 + defaultRadius, mScreenHeight / 2 + defaultRadius);
                mPaint.setColor(Color.parseColor("#FFCC33"));
                mPaint.setStrokeWidth(4);
                AcrBean acrBean = acrList.get(i);
                int start = acrBean.getStart();
                int start2 = acrBean.getStart2();

                canvas.drawArc(mRectF, start, acrBean.getEnd(), false, mPaint);
                canvas.drawArc(mRectF, start2, acrBean.getEnd2(), false, mPaint);
                if (start >= 360) {
                    start = 0;
                }
                acrBean.setStart(start + 1);
                if (start2 >= 360) {
                    start2 = 0;
                }
                acrBean.setStart2(start2 + 1);
            }
            
            if (!isStop) {
                invalidate();
            }
        } catch (Exception e) {

        }
    }

 private void saveArc() {
        for (int i = 0; i < mCircleCount; i++) {
            Random random = new Random();
            int start = random.nextInt(360);
            int end = 5 + (int) (Math.random() * 15);
            int end2 = 5 + (int) (Math.random() * 15);

            acrList.add(new AcrBean(start, end, start + 180, end2));
        }
    }

  class AcrBean {
        private int start;
        private int end;
        private int start2;
        private int end2;

        public AcrBean(int start, int end, int start2, int end2) {
            this.start = start;
            this.end = end;
            this.start2 = start2;
            this.end2 = end2;
        }

        public int getStart2() {
            return start2;
        }

        public void setStart2(int start2) {
            this.start2 = start2;
        }

        public int getEnd2() {
            return end2;
        }

        public void setEnd2(int end2) {
            this.end2 = end2;
        }


        public int getStart() {
            return start;
        }

        public void setStart(int start) {
            this.start = start;
        }

        public int getEnd() {
            return end;
        }

        public void setEnd(int end) {
            this.end = end;
        }
    }

ondraw中的具体操作;

初始化了一个 弧线最短长度为100dp,然后根据每次循环画圆的操作来扩展他的长度,上面提到的间隔也是在不断循环画圆的时候来增加间隔距离,这里为了保证弧线的起始点和终点距离跟同一个圆上的其他起始点距离一样,也做了额外操作;

 public void stopAnim() {
        isStop = true;
        invalidate();
    }

    public void startAnim() {
        isStop = false;
        invalidate();
    }

对外暴露的开始动画和结束动画的方法。

下面看下动态添加view并做动画的一个操作吧

 private void initData() {
        for (int i = 0; i < 20; i++) {
            imagePathList.add("");
        }

    }
 
private void addViewStartAnim(int position) {
        if (isStopAnim) {
            return;
        }
        if (position >= imagePathList.size()) {
            addViewStartAnim(0);
            return;
        }

        View view = LayoutInflater.from(mContext).inflate(R.layout.layout_add_photo_item, null);
        int width = ScreenUtil.dip2px(mContext, 300);
        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(width, width);
        layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
        view.setLayoutParams(layoutParams);
        ImageView ivPhoto = view.findViewById(R.id.iv_photo);

        ivPhoto.setImageResource(R.mipmap.ic_launcher);
        Animation animation = AnimationUtils.loadAnimation(mContext, R.anim.compose_photo_rotation_scale);
        animation.setAnimationListener(new AnimListener());
        addRlAnimView(view);

        animation.setFillAfter(true);
        view.startAnimation(animation);
        position++;
        int pos = position;
        mHandler.postDelayed(() -> addViewStartAnim(pos), 500);

    }
  public void addRlAnimView(View view) {
        mRlAnim.addView(view);
    }
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:duration="5000"
        android:fromXScale="1"
        android:fromYScale="1"
        android:interpolator="@android:anim/decelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="0"
        android:toYScale="0"/>

    <rotate
        android:duration="5000"
        android:fromDegrees="0"
        android:interpolator="@android:anim/decelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="720"
        />

    <alpha
        android:duration="500"
        android:fromAlpha="0"
        android:toAlpha="1"
        />
</set>

这里也一点不复制,通过打气筒创建布局,我给布局中的图片加了一个旋转缩放的动画,旋转点为他的父view的中心点,这样子就能看成他是一个漩涡状态了!自己看着感觉还挺炫酷的。

整体效果真的是一点都不卡的!!是gif录制的问题

有兴趣的可以参考下demo

https://download.****.net/download/l_junpeng/10946278