zoukankan      html  css  js  c++  java
  • 缩放圆效果

    在上一次【https://www.cnblogs.com/webor2006/p/9044535.html】已经学习了补间动画的使用了,今天来学习一下属性动画,用这来实现有个官方loading【https://github.com/webor2006/AVLoadingIndicatorView】效果中的两种效果,如下:

    其中这次实现的效果是它:

    绘制静态圆:

    先不考虑动画效果,将静态圆给绘制出来,如下:

    然后在xml中应用它:

    要绘制圆先要定好圆心和着么,而圆心则以控件View的中心为准,半径取控件View宽高较少值的一半既可,具体如下:

    编译运行:

    圆的缩放和透明度改变:

    接着给圆加上动画,这里采用属性动画的ValueAnimator值变化来实现,先来实现圆的缩放,也就是此时圆的半径值需要写成活的了,如下:

    而原初始化半径为5,需要通过动画来不断的来增加半径的值,下面来初始化一个动画:

    应该是到View较少宽度的一半,因为这个变化指的是圆的半径,所以需要用一个变量来记录其宽高的较小值:

    /**
     * 缩放圆效果--圆的缩放和透明度改变
     */
    public class BallScaleView extends View {
    
        private Paint paint;
        private int viewWidth, viewHeight;
        /* 圆的半径,缩放主要是来控制它的变化 */
        private float radius;
        /* View宽高的较小值 */
        private int length;
    
        public BallScaleView(Context context) {
            this(context, null);
        }
    
        public BallScaleView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public BallScaleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            paint = new Paint();
            paint.setColor(Color.WHITE);
            paint.setAntiAlias(true);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            viewWidth = w;
            viewHeight = h;
            length = Math.min(viewWidth, viewHeight);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawCircle(viewWidth / 2, viewHeight / 2, radius, paint);
        }
    
        private void prepareAnimators() {
            //值动画:可以处理一个值到另一个值的变化,处理圆的缩放
            ValueAnimator scaleValueAnimator = ValueAnimator.ofFloat(5, length / 2);
            scaleValueAnimator.setDuration(2000);
            scaleValueAnimator.setRepeatCount(ValueAnimator.INFINITE);//设置动画重复无限次
            scaleValueAnimator.start();
            scaleValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    radius = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
        }
    }

    然后需要调用一下动画的执行方法,这里放到获得控件宽高的回调方法最合适,如下:

    编译运行:

    由于截屏的原因看着不太顺畅,实际效果是没有这个问题的,另外在显示上需要做一个小优化,就是目前放大最大时紧贴控制边缘了不太好看,如下:

    所以最大半径应该得往回减一点,如下:

    目前只加了一个放大效果,还需加一个透明渐变的效果,同样的实现逻辑,看下面:

    /**
     * 缩放圆效果--圆的缩放和透明度改变
     */
    public class BallScaleView extends View {
    
        private Paint paint;
        private int viewWidth, viewHeight;
        /* 圆的半径,缩放主要是来控制它的变化 */
        private float radius;
        /* View宽高的较小值 */
        private int length;
        /* 透明度 */
        private int alpha;
    
        public BallScaleView(Context context) {
            this(context, null);
        }
    
        public BallScaleView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public BallScaleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            paint = new Paint();
            paint.setColor(Color.WHITE);
            paint.setAntiAlias(true);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            viewWidth = w;
            viewHeight = h;
            length = Math.min(viewWidth, viewHeight);
            prepareAnimators();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            paint.setAlpha(alpha);
            canvas.drawCircle(viewWidth / 2, viewHeight / 2, radius, paint);
        }
    
        private void prepareAnimators() {
            //值动画:可以处理一个值到另一个值的变化,处理圆的缩放
            ValueAnimator scaleValueAnimator = ValueAnimator.ofFloat(5, (length / 2) - 5);
            scaleValueAnimator.setDuration(2000);
            scaleValueAnimator.setRepeatCount(ValueAnimator.INFINITE);//设置动画重复无限次
            scaleValueAnimator.start();
            scaleValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    radius = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
    
            //控制圓的透明度
            ValueAnimator alphaValueAnimator = ValueAnimator.ofInt(255, 0);
            alphaValueAnimator.setDuration(2000);
            alphaValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
            alphaValueAnimator.start();
            alphaValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    alpha = (int) animation.getAnimatedValue();
                    invalidate();
                }
            });
        }
    }

    编译运行:

    多个圆的缩放渐变处理:

    单个圆的效果实现了,接下来效果就得进一步,来实现它了:

    很明显是由多个圆组成的,那倒底是几个圆了貌似用肉眼无法得知,这里不卖关子了,实际是由3个圆按一定的时间顺序进行相同动作的执行来达到此效果的,所以绘制需要放到循环当中,如下:

    那想一想如何来实现多个圆的放大效果呢?其实思路也不难,绘制需要的alpha、radius两个信息应该针对不同的圆进行相应的设置,另外三个圆动画执行是有一个先后顺序的,所以这里先定义一个三个元素的数组用来存放动画执行的延时时间,如下:

    然后一个圆对应一个动画,所以也需要放到循环当中:

    然后需要给动画加一个延时,如下:

    接着需要像延时一样,将radius初始化的半径也定义到数组当中,如下:

    /**
     * 缩放圆效果--多个圆的缩放渐变处理
     */
    public class BallScaleView extends View {
    
        private Paint paint;
        private int viewWidth, viewHeight;
        /* 圆的半径,缩放主要是来控制它的变化,每个圆的半径都从5开始 */
        private static final float[] RADIUS = new float[]{5, 5, 5};
        /* View宽高的较小值 */
        private int length;
        /* 透明度 */
        private int alpha;
        /* 通过延迟来执行绽放来达到多个圆的缩放渐变效果 */
        private long[] DELAYS = new long[]{0, 200, 400};
    
        public BallScaleView(Context context) {
            this(context, null);
        }
    
        public BallScaleView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public BallScaleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            paint = new Paint();
            paint.setColor(Color.WHITE);
            paint.setAntiAlias(true);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            viewWidth = w;
            viewHeight = h;
            length = Math.min(viewWidth, viewHeight);
            prepareAnimators();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            for (int i = 0; i < 3; i++) {
                paint.setAlpha(alpha);
                canvas.drawCircle(viewWidth / 2, viewHeight / 2, RADIUS[i], paint);
            }
        }
    
        private void prepareAnimators() {
            for (int i = 0; i < 3; i++) {
                //值动画:可以处理一个值到另一个值的变化,处理圆的缩放
                ValueAnimator scaleValueAnimator = ValueAnimator.ofFloat(5, (length / 2) - 5);
                scaleValueAnimator.setDuration(2000);
                scaleValueAnimator.setRepeatCount(ValueAnimator.INFINITE);//设置动画重复无限次
                scaleValueAnimator.setStartDelay(DELAYS[i]);
                scaleValueAnimator.start();
                final int index = i;
                scaleValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        RADIUS[index] = (float) animation.getAnimatedValue();
                        invalidate();
                    }
                });
    
                //控制圓的透明度
                ValueAnimator alphaValueAnimator = ValueAnimator.ofInt(255, 0);
                alphaValueAnimator.setDuration(2000);
                alphaValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
                alphaValueAnimator.setStartDelay(DELAYS[i]);
                alphaValueAnimator.start();
                alphaValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        alpha = (int) animation.getAnimatedValue();
                        invalidate();
                    }
                });
            }
        }
    }

    同样的对于alpha也需要定义到数组当中,其目的就是各个圆的属性都是单独处理的,不会因为其它圆的动画播放影响到当前圆的播入,所以:

    /**
     * 缩放圆效果--多个圆的缩放渐变处理
     */
    public class BallScaleView extends View {
    
        //constants
        /* 圆的半径,缩放主要是来控制它的变化,每个圆的半径都从5开始 */
        private static final float[] RADIUS = new float[]{5, 5, 5};
        /* 透明度,每个圆的透明度都从255开始 */
        private int[] ALPHAS = new int[]{255, 255, 255};
        /* 通过延迟来执行绽放来达到多个圆的缩放渐变效果 */
        private long[] DELAYS = new long[]{0, 200, 400};
    
        //variables
        private Paint paint;
        private int viewWidth, viewHeight;
        /* View宽高的较小值 */
        private int length;
    
        public BallScaleView(Context context) {
            this(context, null);
        }
    
        public BallScaleView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public BallScaleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            paint = new Paint();
            paint.setColor(Color.WHITE);
            paint.setAntiAlias(true);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            viewWidth = w;
            viewHeight = h;
            length = Math.min(viewWidth, viewHeight);
            prepareAnimators();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            for (int i = 0; i < 3; i++) {
                paint.setAlpha(ALPHAS[i]);
                canvas.drawCircle(viewWidth / 2, viewHeight / 2, RADIUS[i], paint);
            }
        }
    
        private void prepareAnimators() {
            for (int i = 0; i < 3; i++) {
                //值动画:可以处理一个值到另一个值的变化,处理圆的缩放
                ValueAnimator scaleValueAnimator = ValueAnimator.ofFloat(5, (length / 2) - 5);
                scaleValueAnimator.setDuration(2000);
                scaleValueAnimator.setRepeatCount(ValueAnimator.INFINITE);//设置动画重复无限次
                scaleValueAnimator.setStartDelay(DELAYS[i]);
                scaleValueAnimator.start();
                final int index = i;
                scaleValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        RADIUS[index] = (float) animation.getAnimatedValue();
                        invalidate();
                    }
                });
    
                //控制圓的透明度
                ValueAnimator alphaValueAnimator = ValueAnimator.ofInt(255, 0);
                alphaValueAnimator.setDuration(2000);
                alphaValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
                alphaValueAnimator.setStartDelay(DELAYS[i]);
                alphaValueAnimator.start();
                alphaValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        ALPHAS[index] = (int) animation.getAnimatedValue();
                        invalidate();
                    }
                });
            }
        }
    }

    编译运行:

    嗯~~效果不错,不过既然现在半径和透明度都可以单独控制,那每个圆的颜色那也可以单独控制呀,那试着给每个圆定义不同的颜色来看一下效果有没有酷炫炸呢?

    编译运行:

    我去!!颜色亮瞎眼~~

    单个圆空心效果:

    好,接下来实现这个效果:

    实现起来相当之easy,只要更改画笔的样式既可,回到单个圆效果代码,修改如下:

    /**
     * 缩放圆效果--单个圆空心效果
     */
    public class BallScaleView extends View {
    
        private Paint paint;
        private int viewWidth, viewHeight;
        /* View宽高的较小值 */
        private int length;
        /* 圆的半径,缩放主要是来控制它的变化 */
        private float radius;
        /* 透明度 */
        private int alpha;
    
        public BallScaleView(Context context) {
            this(context, null);
        }
    
        public BallScaleView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public BallScaleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            paint = new Paint();
            paint.setColor(Color.WHITE);
            paint.setAntiAlias(true);
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(2);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            viewWidth = w;
            viewHeight = h;
            length = Math.min(viewWidth, viewHeight);
            prepareAnimators();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            paint.setAlpha(alpha);
            canvas.drawCircle(viewWidth / 2, viewHeight / 2, radius, paint);
        }
    
        private void prepareAnimators() {
            //值动画:可以处理一个值到另一个值的变化,处理圆的缩放
            ValueAnimator scaleValueAnimator = ValueAnimator.ofFloat(5, (length / 2) - 5);
            scaleValueAnimator.setDuration(2000);
            scaleValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
            scaleValueAnimator.start();
            scaleValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    radius = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
    
            //控制圓的透明度
            ValueAnimator alphaValueAnimator = ValueAnimator.ofInt(255, 0);
            alphaValueAnimator.setDuration(2000);
            alphaValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
            alphaValueAnimator.start();
            alphaValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    alpha = (int) animation.getAnimatedValue();
                    invalidate();
                }
            });
        }
    }

    编译运行:

    多个圆空心效果:

    最后再来实现多个圆的空心效果:

    同样的方法,也只需要修改画笔的样式既可,回到多个圆效果代码,修改如下:

    /**
     * 缩放圆效果--多个圆空心效果
     */
    public class BallScaleView extends View {
    
        //constants
        /* 圆的半径,缩放主要是来控制它的变化,每个圆的半径都从5开始 */
        private static final float[] RADIUS = new float[]{5, 5, 5};
        /* 透明度,每个圆的透明度都从255开始 */
        private int[] ALPHAS = new int[]{255, 255, 255};
        /* 通过延迟来执行绽放来达到多个圆的缩放渐变效果 */
        private long[] DELAYS = new long[]{0, 200, 400};
        /* 给不同的圆着上不同的颜色使其酷炫炸 */
        private int[] COLORS = new int[]{Color.RED, Color.GREEN, Color.BLUE};
    
        //variables
        private Paint paint;
        private int viewWidth, viewHeight;
        /* View宽高的较小值 */
        private int length;
    
        public BallScaleView(Context context) {
            this(context, null);
        }
    
        public BallScaleView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public BallScaleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            paint = new Paint();
            paint.setColor(Color.WHITE);
            paint.setAntiAlias(true);
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(2);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            viewWidth = w;
            viewHeight = h;
            length = Math.min(viewWidth, viewHeight);
            prepareAnimators();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            for (int i = 0; i < 3; i++) {
                paint.setAlpha(ALPHAS[i]);
                paint.setColor(COLORS[i]);
                canvas.drawCircle(viewWidth / 2, viewHeight / 2, RADIUS[i], paint);
            }
        }
    
        private void prepareAnimators() {
            for (int i = 0; i < 3; i++) {
                //值动画:可以处理一个值到另一个值的变化,处理圆的缩放
                ValueAnimator scaleValueAnimator = ValueAnimator.ofFloat(5, (length / 2) - 5);
                scaleValueAnimator.setDuration(2000);
                scaleValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
                scaleValueAnimator.setStartDelay(DELAYS[i]);
                scaleValueAnimator.start();
                final int index = i;
                scaleValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        RADIUS[index] = (float) animation.getAnimatedValue();
                        invalidate();
                    }
                });
    
                //控制圓的透明度
                ValueAnimator alphaValueAnimator = ValueAnimator.ofInt(255, 0);
                alphaValueAnimator.setDuration(2000);
                alphaValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
                alphaValueAnimator.setStartDelay(DELAYS[i]);
                alphaValueAnimator.start();
                alphaValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        ALPHAS[index] = (int) animation.getAnimatedValue();
                        invalidate();
                    }
                });
            }
        }
    }

    編译运行:

    到此效果完美实现,关于属性动画目前咱们只使用了ValueAnimator,在未来还会接触到其它的一些属性动画滴,慢慢来~

  • 相关阅读:
    hotmail 收不到邮件的问题
    getaddrinfo 报错 Invalid value for ai_flags
    Avoiding Common Networking Mistakes
    关掉标准输出
    不需要 root 权限的 ping
    select 的问题
    Behavior Tree 用 Lua 实现一个最简行为树
    对 UDP 的一些思考
    Windows UDP sockets: recvfrom() fails with error 10054
    和等于某个数的所有组合
  • 原文地址:https://www.cnblogs.com/webor2006/p/9140811.html
Copyright © 2011-2022 走看看