缩放圆效果

在上一次【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,在未来还会接触到其它的一些属性动画滴,慢慢来~

原文地址:https://www.cnblogs.com/webor2006/p/9140811.html