Android Canvas实际操作

本文详细介绍了如何使用Android Canvas进行实际操作,包括利用ValueAnimator实现小球旋转动画,通过OvershootInterpolator实现小球扩展与聚合效果,以及创建水波纹动画。关键步骤包括计算小球的圆心坐标、设置旋转角度、使用插值器以及调整strokeWidth来模拟水波纹。
摘要由CSDN通过智能技术生成

本篇我们来看canvas的实际应用,这里会应用到属性动画ValueAnimator类,下面先看一下具体的效果
在这里插入图片描述
上述效果图中主要由三部分组成,第一,绘制6个小球并旋转,第二,6个小球先扩展然后收缩,第三,水波纹效果,展示一张图片。

1)旋转部分
首先,将6个小球分别绘制到屏幕上,调用 canvas.drawCircle()方法来绘制小球,根据api的参数,这里我们需要计算出每个小球的圆心坐标,6个小球是在一个圆内,也就是说将一个圆均分为了6份,而整个圆是360°,那么两小球之间的夹角就是
float rotate = Math.PI*2/ballColor.length 这里ballColor是定义的6个小球颜色的数组int ballColor[],所有小球之间的夹角就是

   //计算角度
        float rotate = (float) (Math.PI * 2 / ballColor.length);
        for (int i = 0; i < ballColor.length; i++) {
            //计算其他小球对应的角度
             float angel = rotate * i;
         }
    

角度有了,如何计算出每个小球的圆心坐标点呢,我们以一张图来说明坐标的计算在这里插入图片描述
∠ABC 就是上面我们计算出的角度值angel ,AB长是圆环的半径,由我们自己定义,那么A点的X坐标就是Math.cos(angel) *radiusCurBall(圆环半径)+centerX,相应的Y坐标就是Math.sin(angel) *radiusCurBall+centerY ,这里的centerX和centerY 表示画布的中心点坐标,这样每个小球的圆心坐标就有了。

private void drawCirlce(Canvas canvas) {
        //计算角度
        float rotate = (float) (Math.PI * 2 / ballColor.length);
        for (int i = 0; i < ballColor.length; i++) {
            //计算其他小球对应的角度
            float angel = rotate * i;
            float cx = (float) (Math.cos(angel) * radiusCurBall + centerX);
            float cy = (float) (Math.sin(angel) * radiusCurBall + centerY);

            paintSm.setColor(ballColor[i]);
            canvas.drawCircle(cx, cy, radiusSmBall, paintSm);
        }
    }

小球绘制完毕后,该执行旋转动画了,这里采用ValueAnimator来执行,每个小球的旋转角度是从0到360°,调用valueAnimator.addUpdateListener,获取到动画在执行过程中的角度,将该角度传递到计算小球角度的逻辑代码中,从而完成小球的旋转

valueAnimator = ValueAnimator.ofFloat(0f, (float) (Math.PI * 2));
valueAnimator.setRepeatCount(2);
valueAnimator.setDuration(rotateDuration);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        rotateAngel = (float) animation.getAnimatedValue();
        invalidate();
    }
});
Animator.start();

rotateAngel 就是获取的实时旋转角度,修改绘制小球drawCirlce()方法

 private void drawCirlce(Canvas canvas) {
        //计算角度
        float rotate = (float) (Math.PI * 2 / ballColor.length);
        for (int i = 0; i < ballColor.length; i++) {
            //计算其他小球对应的角度
//            float angel = rotate * i;
            float angel = rotate * i + rotateAngel;
            float cx = (float) (Math.cos(angel) * radiusCurBall + centerX);
            float cy = (float) (Math.sin(angel) * radiusCurBall + centerY);
            paintSm.setColor(ballColor[i]);
            canvas.drawCircle(cx, cy, radiusSmBall, paintSm);
        }
    }

这样,小球就开始旋转了。
下面再来看一下第二个过程,扩展和聚合。在该动画中,我们使用到了一个新的插值器OvershootInterpolator,它表示动画在执行过程中,会向前甩一定的值,然后会回到原来位置,在效果图中,小球是先向外扩散,然后再聚合,那么动画的执行过程就是小球半径到圆环半径。

valueAnimator = ValueAnimator.ofFloat(radiusSmBall, radiusBigBall);
valueAnimator.setDuration(rotateDuration);
valueAnimator.setInterpolator(new OvershootInterpolator(5f));
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        radiusCurBall = (float) animation.getAnimatedValue();
        invalidate();
    }
});
valueAnimator.reverse();

这里我们调用了reverse()方法,将动画反向执行,从而达到效果图中先扩散再聚合的目的。
最后再来看一下水波纹效果,实际上是不断的绘制一个空心圆,调整其strokeWidth的大小,使其达到水波纹特效,再第二步操作中,动画执行完后,当前圆环的半径就是小球的半径值,那么水波纹动画的起始值就是该圆环半径,最终值是手机斜对角线长度的一半。
这里有一点需要留意,在上一篇的文章中我们提到过canvas.drawCircle()的使用,如果设置了paint的strokeWidth值,那么圆的实际半径是内圆的半径+strokeWidth/2

valueAnimator = ValueAnimator.ofFloat(radiusSmBall, mDistane);
valueAnimator.setDuration(rotateDuration);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        radiusPole = (float) animation.getAnimatedValue();
        invalidate();
    }
});
valueAnimator.start();

再来看一下绘制水波纹

float strokeWidth = mDistane - radiusPole;
float realRadius = radiusPole + strokeWidth / 2;
paintPole.setStrokeWidth(strokeWidth);
canvas.drawCircle(centerX, centerY, realRadius, paintPole);

看一下完整实现

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.view.animation.OvershootInterpolator;

public class AnimView extends View {

    //6个小球半径
    private float radiusSmBall = 18;
    //旋转圆半径
    private float radiusBigBall = 90;
    //扩散圆半径
    private float radiusCurBall = radiusBigBall;
    //水波纹半径
    private float radiusPole;
    //旋转角度
    private float rotateAngel;
    //小球画笔
    private Paint paintSm;
    //水波纹画笔
    private Paint paintPole;
    //背景色
    private int bgColor = Color.WHITE;
    //6个小球颜色数组
    private int ballColor[];
    //中心点坐标
    private float centerX;
    private float centerY;

    private SplashState state;

    private ValueAnimator valueAnimator;

    private long rotateDuration = 900;
    //屏幕斜对角距离,水波纹的最大半径
    private float mDistane;

    public AnimView(Context context) {
        this(context, null);
    }

    public AnimView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AnimView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    //动画效果,1.6个小球转动,2.6个小球扩散、聚合,3.水波纹效果
    private void init(Context context) {
        ballColor = context.getResources().getIntArray(R.array.splash_circle_colors);
        paintSm = new Paint(Paint.ANTI_ALIAS_FLAG);

        paintPole = new Paint();
        paintPole.setStyle(Paint.Style.STROKE);
        paintPole.setColor(bgColor);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (state == null) {
            state = new TurnState();
        }
        state.drawState(canvas);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        centerX = w * 1f / 2;
        centerY = h * 1f / 2;
        mDistane = (float) (Math.hypot(w, h)/2);

    }

    /**
     * 旋转
     */
    private class TurnState extends SplashState {

        public TurnState() {
            valueAnimator = ValueAnimator.ofFloat(0f, (float) (Math.PI * 2));
            valueAnimator.setRepeatCount(2);
            valueAnimator.setDuration(rotateDuration);
            valueAnimator.setInterpolator(new LinearInterpolator());
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    rotateAngel = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            valueAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    state = new MergeState();
                }
            });
            valueAnimator.start();
        }

        @Override
        void drawState(Canvas canvas) {
            drawBackground(canvas);
            drawCirlce(canvas);
        }
    }
    /**
     * 扩散、收缩
     */
    private class MergeState extends SplashState {
        public MergeState() {
            valueAnimator = ValueAnimator.ofFloat(radiusSmBall, radiusBigBall);
            valueAnimator.setDuration(rotateDuration);
            valueAnimator.setInterpolator(new OvershootInterpolator(5f));
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    radiusCurBall = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            valueAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    state = new PoleState();
                }
            });
            valueAnimator.reverse();
        }

        @Override
        void drawState(Canvas canvas) {
            drawBackground(canvas);
            drawCirlce(canvas);
        }
    }
    /**
     * 水波纹
     */
    private class PoleState extends SplashState {

        public PoleState() {
            valueAnimator = ValueAnimator.ofFloat(radiusSmBall, mDistane);
            valueAnimator.setDuration(rotateDuration);
            valueAnimator.setInterpolator(new LinearInterpolator());
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    radiusPole = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            valueAnimator.start();
        }

        @Override
        void drawState(Canvas canvas) {
            drawBackground(canvas);
        }
    }


    private void drawBackground(Canvas canvas) {
        //表示该绘制水波纹
        if (radiusPole > 0) {
            float strokeWidth = mDistane - radiusPole;
            float realRadius = radiusPole + strokeWidth / 2;
            paintPole.setStrokeWidth(strokeWidth);
            canvas.drawCircle(centerX, centerY, realRadius, paintPole);

        } else {
            canvas.drawColor(bgColor);
        }
    }

    private void drawCirlce(Canvas canvas) {
        //计算角度
        float rotate = (float) (Math.PI * 2 / ballColor.length);
        for (int i = 0; i < ballColor.length; i++) {
            //计算其他小球对应的角度
//            float angel = rotate * i;
            float angel = rotate * i + rotateAngel;
            float cx = (float) (Math.cos(angel) * radiusCurBall + centerX);
            float cy = (float) (Math.sin(angel) * radiusCurBall + centerY);

            paintSm.setColor(ballColor[i]);
            canvas.drawCircle(cx, cy, radiusSmBall, paintSm);
        }
    }

    abstract class SplashState {
        abstract void drawState(Canvas canvas);
    }
}

以及xml布局

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <ImageView
        android:scaleType="fitXY"
        android:src="@mipmap/content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <com.example.paintapp.AnimView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值