Android实现旋转动画的两种方式案例详解

目录

  • 练习案例
  • 效果展示
  • 前期准备
  • 自定义 View java代码编写
    • 方法一
    • 方法二
  • 易错点总结:

    练习案例 视差动画 - 雅虎新闻摘要加载

    效果展示 Android实现旋转动画的两种方式案例详解
    文章图片


    前期准备 第一步:准备好颜色数组 res => values => colors.xml
    #FF9600#02D1AC#FFD200#00C6FF#00E099#FF3891 @color/orange@color/aqua@color/yellow@color/bule@color/green@color/pink

    【Android实现旋转动画的两种方式案例详解】Android实现旋转动画的两种方式案例详解
    文章图片


    自定义 View java代码编写
    方法一
    关键思想: 属性动画 + 计算圆心
    Android实现旋转动画的两种方式案例详解
    文章图片

    package com.wust.mydialog; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; import android.view.animation.LinearInterpolator; import androidx.annotation.Nullable; /** * ClassName: com.wust.mydialog.MyRotateView
    * Description:
    * date: 2021/8/7 12:13
    * * @author yiqi
    * @QQ 1820762465 * @微信 yiqiideallife * @技术交流QQ群 928023749 */public class MyRotateView extends View { //设置旋转间隔时间private int SPLASH_CIRCLE_ROTATE_TIME = 3000; //设置中心圆半径private float CENTER_CIRCLE_RADIUS; private float SMALL_CIRCLE_RADIUS; private float mCurrentSingle = 0f; private int[] mColorArray; private Paint mCirclePaint; private ValueAnimator va; public MyRotateView(Context context) {super(context); } public MyRotateView(Context context, @Nullable AttributeSet attrs) {super(context, attrs); } public MyRotateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr); } @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); //初始化参数initParams(width,height); setMeasuredDimension(width,height); } private void initParams(int w, int h) {//设置中心圆半径CENTER_CIRCLE_RADIUS = 1/4.0f * w; //设置小圆的半径SMALL_CIRCLE_RADIUS = 1/25.0f * w; //获取小球颜色mColorArray = getResources().getIntArray(R.array.splash_circle_colors); //初始化画笔mCirclePaint = new Paint(); mCirclePaint.setDither(true); mCirclePaint.setAntiAlias(true); } @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas); //绘制圆drawSplashCircle(canvas); } private void drawSplashCircle(Canvas canvas) {//设置属性动画,让小圆转起来//这里得注意,是个坑,你如果不判断那球就不会动 因为会陷入死循环 值动画将值设置为0 -> invalidate()重绘 -> 执行draw 又将值设为0if (va == null){va = ObjectAnimator.ofFloat(0f, 2 * (float) Math.PI); va.setDuration(SPLASH_CIRCLE_ROTATE_TIME); va.setRepeatCount(ValueAnimator.INFINITE); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mCurrentSingle = (float) animation.getAnimatedValue(); //System.out.println("mCurrentSingle ->" + mCurrentSingle); invalidate(); }}); va.setInterpolator(new LinearInterpolator()); va.start(); } //计算每个小球的间隔double spaceAngle = Math.PI*2/mColorArray.length; for (int i = 0; i < mColorArray.length; i++) {//为 每个球 画笔 设置颜色mCirclePaint.setColor(mColorArray[i]); //利用 勾股定理 计算 小圆 中心点float cx = getWidth()/2 + (float) (CENTER_CIRCLE_RADIUS*Math.cos(spaceAngle*i+mCurrentSingle)); float cy = getHeight()/2 + (float) (CENTER_CIRCLE_RADIUS*Math.sin(spaceAngle*i+mCurrentSingle)); canvas.drawCircle(cx,cy,SMALL_CIRCLE_RADIUS,mCirclePaint); }}}


    方法二
    关键思想:旋转画布法
    package com.wust.mydialog; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; import android.view.animation.LinearInterpolator; import androidx.annotation.Nullable; /** * ClassName: com.wust.mydialog.MyRotateView
    * Description:
    * date: 2021/8/7 12:13
    * * @author yiqi
    * @QQ 1820762465 * @微信 yiqiideallife * @技术交流QQ群 928023749 */public class MyRotateView extends View { //设置旋转间隔时间private int SPLASH_CIRCLE_ROTATE_TIME = 3000; //设置中心圆半径private float CENTER_CIRCLE_RADIUS; private float SMALL_CIRCLE_RADIUS; private float mCurrentSingle = 0f; private int[] mColorArray; private Paint mCirclePaint; private ValueAnimator va; public MyRotateView(Context context) {super(context); } public MyRotateView(Context context, @Nullable AttributeSet attrs) {super(context, attrs); } public MyRotateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr); } @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); //初始化参数initParams(width,height); setMeasuredDimension(width,height); } private void initParams(int w, int h) {//设置中心圆半径CENTER_CIRCLE_RADIUS = 1/4.0f * w; //设置小圆的半径SMALL_CIRCLE_RADIUS = 1/25.0f * w; //获取小球颜色mColorArray = getResources().getIntArray(R.array.splash_circle_colors); //初始化画笔mCirclePaint = new Paint(); mCirclePaint.setDither(true); mCirclePaint.setAntiAlias(true); } @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas); //绘制圆drawSplashCircle(canvas); } private void drawSplashCircle(Canvas canvas) {//设置属性动画,让小圆转起来//这里得注意,是个坑,你如果不判断那球就不会动 因为会陷入死循环 值动画将值设置为0 -> invalidate()重绘 -> 执行draw 又将值设为0if (va == null){//va = ObjectAnimator.ofFloat(0f, 2 * (float) Math.PI); va = ObjectAnimator.ofFloat(0f, 360.0f); va.setDuration(SPLASH_CIRCLE_ROTATE_TIME); va.setRepeatCount(ValueAnimator.INFINITE); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mCurrentSingle = (float) animation.getAnimatedValue(); //System.out.println("mCurrentSingle ->" + mCurrentSingle); invalidate(); }}); va.setInterpolator(new LinearInterpolator()); va.start(); } //计算每个小球的间隔//double spaceAngle = Math.PI*2/mColorArray.length; double spaceAngle = 360.0d/mColorArray.length; System.out.println("spaceAngle -> " + spaceAngle); //利用旋转画布法canvas.save(); canvas.rotate(mCurrentSingle,getWidth()/2,getHeight()/2); for (int i = 0; i < mColorArray.length; i++) {canvas.rotate((float) spaceAngle,getWidth()/2,getHeight()/2); //为 每个球 画笔 设置颜色mCirclePaint.setColor(mColorArray[i]); //利用 勾股定理 计算 小圆 中心点//float cx = getWidth()/2 + (float) (CENTER_CIRCLE_RADIUS*Math.cos(spaceAngle*i+mCurrentSingle)); //float cy = getHeight()/2 + (float) (CENTER_CIRCLE_RADIUS*Math.sin(spaceAngle*i+mCurrentSingle)); //利用旋转画布法float cx = getWidth()/2 + CENTER_CIRCLE_RADIUS; float cy = getHeight()/2; canvas.drawCircle(cx,cy,SMALL_CIRCLE_RADIUS,mCirclePaint); }canvas.restore(); }}


    易错点总结: 1、canvas.rotate(mCurrentSingle,getWidth()/2,getHeight()/2); 中 第一个参数传的是角度(360度的那种),而 Math.cos(); 中 参数传的是一个弧度(2π的那种)
    2、canvas.rotate() 函数执行之后对后续画布上的操作都是有影响的,所以,得配合 canvas.save(); 和 canvas.restore(); 使用。因此,里面的canvas.rotate((float) spaceAngle,getWidth()/2,getHeight()/2); 中spaceAngle不能乘 i 。
    3、画布的旋转除了 canvas.rotate() 函数 可以实现外,还可以利用矩阵。代码如下:
    //创建矩阵private Matrix mSpaceMatrix; //初始化旋转矩阵mSpaceMatrix = new Matrix(); //初始化旋转矩阵mSpaceMatrix.reset(); mSpaceMatrix.postRotate((float) spaceAngle,getWidth()/2,getHeight()/2); //画布旋转角度canvas.concat(mSpaceMatrix);

    完整代码
    package com.wust.mydialog; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; import android.view.animation.LinearInterpolator; import androidx.annotation.Nullable; /** * ClassName: com.wust.mydialog.MyRotateView
    * Description:
    * date: 2021/8/7 12:13
    * * @author yiqi
    * @QQ 1820762465 * @微信 yiqiideallife * @技术交流QQ群 928023749 */public class MyRotateView extends View { //设置旋转间隔时间private int SPLASH_CIRCLE_ROTATE_TIME = 3000; //设置中心圆半径private float CENTER_CIRCLE_RADIUS; private float SMALL_CIRCLE_RADIUS; private float mCurrentSingle = 0f; private int[] mColorArray; private Paint mCirclePaint; private ValueAnimator va; private Matrix mSpaceMatrix; public MyRotateView(Context context) {super(context); } public MyRotateView(Context context, @Nullable AttributeSet attrs) {super(context, attrs); } public MyRotateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr); } @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); //初始化参数initParams(width,height); setMeasuredDimension(width,height); } private void initParams(int w, int h) {//设置中心圆半径CENTER_CIRCLE_RADIUS = 1/4.0f * w; //设置小圆的半径SMALL_CIRCLE_RADIUS = 1/25.0f * w; //获取小球颜色mColorArray = getResources().getIntArray(R.array.splash_circle_colors); //初始化画笔mCirclePaint = new Paint(); mCirclePaint.setDither(true); mCirclePaint.setAntiAlias(true); //初始化旋转矩阵mSpaceMatrix = new Matrix(); } @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas); //绘制圆drawSplashCircle(canvas); } private void drawSplashCircle(Canvas canvas) {//设置属性动画,让小圆转起来//这里得注意,是个坑,你如果不判断那球就不会动 因为会陷入死循环 值动画将值设置为0 -> invalidate()重绘 -> 执行draw 又将值设为0if (va == null){//va = ObjectAnimator.ofFloat(0f, 2 * (float) Math.PI); va = ObjectAnimator.ofFloat(0f, 360.0f); va.setDuration(SPLASH_CIRCLE_ROTATE_TIME); va.setRepeatCount(ValueAnimator.INFINITE); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mCurrentSingle = (float) animation.getAnimatedValue(); //System.out.println("mCurrentSingle ->" + mCurrentSingle); invalidate(); }}); va.setInterpolator(new LinearInterpolator()); va.start(); } //计算每个小球的间隔//double spaceAngle = Math.PI*2/mColorArray.length; double spaceAngle = 360.0d/mColorArray.length; //初始化旋转矩阵mSpaceMatrix.reset(); mSpaceMatrix.postRotate((float) spaceAngle,getWidth()/2,getHeight()/2); //利用旋转画布法canvas.save(); canvas.rotate(mCurrentSingle,getWidth()/2,getHeight()/2); for (int i = 0; i < mColorArray.length; i++) {//canvas.rotate((float) spaceAngle,getWidth()/2,getHeight()/2); //System.out.println("spaceAngle -> " + spaceAngle); canvas.concat(mSpaceMatrix); //为 每个球 画笔 设置颜色mCirclePaint.setColor(mColorArray[i]); //利用 勾股定理 计算 小圆 中心点//float cx = getWidth()/2 + (float) (CENTER_CIRCLE_RADIUS*Math.cos(spaceAngle*i+mCurrentSingle)); //float cy = getHeight()/2 + (float) (CENTER_CIRCLE_RADIUS*Math.sin(spaceAngle*i+mCurrentSingle)); //利用旋转画布法float cx = getWidth()/2 + CENTER_CIRCLE_RADIUS; float cy = getHeight()/2; canvas.drawCircle(cx,cy,SMALL_CIRCLE_RADIUS,mCirclePaint); }canvas.restore(); }}

    注意事项:
    1、canvas.concat(mSpaceMatrix); 对画布的操作也会对后面进行影响
    2、Android中Matrix的set、pre、post的区别
    说set、pre、post的区别之前,先说说Matrix。
    Matrix包含一个3 X 3的矩阵,专门用于图像变换匹配。
    Matrix提供了四种操作:
    • translate(平移)
    • rotate(旋转)
    • scale(缩放)
    • skew(倾斜)
    也就是说这4种操作都是对这个3 X 3的矩阵设值来达到变换的效果。
    Matrix没有结构体,它必须被初始化,通过reset或set方法。
    OK,Matrix介绍完了,我们来看看set、pre、post的区别。
    pre是在队列最前面插入,post是在队列最后面追加,而set先清空队列在添加(这也是上文提到的“Matrix没有结构体,它必须被初始化,通过reset或set方法”的原因)。
    下面通过一些例子具体说明:
    1. matrix.preScale(2f,1f);
    2. matrix.preTranslate(5f, 0f);
    3. matrix.postScale(0.2f, 1f);
    4. matrix.postTranslate(0.5f, 0f);
    执行顺序:translate(5, 0) -> scale(2f, 1f) -> scale(0.2f, 1f) -> translate(0.5f, 0f)
    1. matrix.postTranslate(2f, 0f);
    2. matrix.preScale(0.2f, 1f);
    3. matrix.setScale(1f, 1f);
    4. matrix.postScale(5f, 1f);
    5. matrix.preTranslate(0.5f, 0f);
    执行顺序:translate(0.5f, 0f) -> scale(1f, 1f) -> scale(5f, 1)
    到此这篇关于Android实现旋转动画的两种方式的文章就介绍到这了,更多相关android旋转动画内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

      推荐阅读