零、前言:本文知识点

  • ValueAnimator的认识与使用
  • 估值器TypeEvaluator的自定义与使用
  • 插值器TimeInterpolator的自定义与使用
  • Path于Animator的结合使用
  • ObjectAnimator的自定义与使用
  • TimeAnimator的使用
  • AnimatorSet动画集合的使用
  • Animator家族的监听器介绍与使用
  • Animator家族在xml中的使用

一直用动画,貌似还没有好好地总结一下,趁有空,总结一波
所谓动画,就是不停变化,在视觉上达到连续的效果
Animator的体系并不复杂,但内部实现挺复杂的,很多类常年埋没于底层,不见天日
如:PropertyValuesHolder及其子类Keyframes族Keyframe族KeyframeSet族
今天试着读了一下源码,基本上读的懵懵懂懂,总的思路算是把握了


第一节:ValueAnimator的使用

一、简单的使用

0.Animator家族简单认识:

Animator是一个抽象类,不可用,只能找它的子类
现在先看非常常用的ValueAnimator

Animator体系.png


1.下面是一段ValueAnimator最简单的使用
ValueAnimator animator = ValueAnimator.ofInt(0, 10);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {Log.e(TAG, animation.getAnimatedValue()+"---");}
});
animator.start();

打印结果分析:

2018-12-26 12:04:09.290 ~ 2018-12-26 12:04:09.584---->584-290=294
默认持续时间是300(源码中定义的),基本一致,在这段时间内不断回调onAnimationUpdate方法
并且animation的值从预定的0~10之间不断变化,这就是ValueAnimator的基本用处
2018-12-26 12:04:09.290 2001-2001/com.toly1994.animator_test E/MainActivity: 0---
2018-12-26 12:04:09.335 2001-2001/com.toly1994.animator_test E/MainActivity: 0---
2018-12-26 12:04:09.351 2001-2001/com.toly1994.animator_test E/MainActivity: 1---
2018-12-26 12:04:09.373 2001-2001/com.toly1994.animator_test E/MainActivity: 1---
2018-12-26 12:04:09.412 2001-2001/com.toly1994.animator_test E/MainActivity: 3---
2018-12-26 12:04:09.439 2001-2001/com.toly1994.animator_test E/MainActivity: 5---
2018-12-26 12:04:09.450 2001-2001/com.toly1994.animator_test E/MainActivity: 5---
2018-12-26 12:04:09.468 2001-2001/com.toly1994.animator_test E/MainActivity: 6---
2018-12-26 12:04:09.484 2001-2001/com.toly1994.animator_test E/MainActivity: 7---
2018-12-26 12:04:09.502 2001-2001/com.toly1994.animator_test E/MainActivity: 8---
2018-12-26 12:04:09.517 2001-2001/com.toly1994.animator_test E/MainActivity: 8---
2018-12-26 12:04:09.534 2001-2001/com.toly1994.animator_test E/MainActivity: 9---
2018-12-26 12:04:09.568 2001-2001/com.toly1994.animator_test E/MainActivity: 9---
2018-12-26 12:04:09.584 2001-2001/com.toly1994.animator_test E/MainActivity: 10---

2.从中衍生的想法

1).不断调用onAnimationUpdate回调
2).可以获取有规律变化的不同的数值
在自定义View中onAnimationUpdate刷新界面,并动态改变数值

简单应用.gif

/*** 作者:张风捷特烈<br/>* 时间:2018/12/26 0026:7:50<br/>* 邮箱:1981462002@qq.com<br/>* 说明:Animator测试View*/
public class AnimatorView extends View {private static final String TAG = "AnimatorView";private Paint mPaint;//画笔private int mRadius = 100;//小球初始半径private ValueAnimator mAnimator;//动画器public AnimatorView(Context context) {this(context, null);}public AnimatorView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}private void init() {mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setColor(0xff94E1F7);mAnimator = ValueAnimator.ofInt(100, 300);mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mRadius= (int) animation.getAnimatedValue();invalidate();}});}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.translate(400, 400);//移动坐标canvas.drawCircle(0, 0, mRadius, mPaint);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:Log.e(TAG, "onTouchEvent: ");mAnimator.start();//点击开启动画break;case MotionEvent.ACTION_UP:}return super.onTouchEvent(event);}
}

其实道理很简单,就是把打印输出换成了刷新视图,而且半径在不断变化


3.常规配置

看一下RESTART(默认)和REVERSE的区别

RESTART REVERSE
mAnimator.setStartDelay(1000);//设置延迟
mAnimator.setRepeatCount(2);//设置重复执行次数
// mAnimator.setRepeatMode(ValueAnimator.RESTART);//重新开始100->300 100->300
mAnimator.setRepeatMode(ValueAnimator.REVERSE);//反转开始100->300 300->100
mAnimator.setDuration(1000);//设置时长

二、ofArgbofObject

颜色变化 颜色大小

1.改变颜色:ofArgb

传入两个颜色(起始色和终止色)

mColorAnimator = ValueAnimator.ofArgb(0xff94E1F7, 0xffF35519);
mColorAnimator.setDuration(500);//设置时长
mColorAnimator.setRepeatCount(1);//设置重复执行次数
mColorAnimator.setRepeatMode(ValueAnimator.REVERSE);mColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mPaint.setColor((Integer) animation.getAnimatedValue());invalidate();}
});

2.如何即改变大小又改变颜色:

ValueAnimator.ofObject + TypeEvaluator

2.1先定义一个类承载数据:Ball(为了演示简洁,使用public属性)
public class Ball {public int color;public int r;public Ball() {}public Ball(int r, int color) {this.color = color;this.r = r;}
}

2.2.创建TypeEvaluator(类型估值器)

TypeEvaluator是确定对象的各个属性如何变化,看下面例子:
这里fraction是分率,startValue和endValue分别是起始和终止对象的状态

public class BallEvaluator implements TypeEvaluator {@Overridepublic Object evaluate(float fraction, Object startValue, Object endValue) {Ball start = (Ball) startValue;//小球初始状态Ball end = (Ball) endValue;//小球终止状态Ball ball = new Ball();//当前小球//半径=初始+分率*(结尾-初始) 比如运动到一半,分率是0.5ball.r = (int) (start.r + fraction * (end.r - start.r));//颜色怎么渐变?ball.color = evaluateColor(fraction, start.color, end.color);return null;}/*** 根据分率计算颜色*/private int evaluateColor(float fraction, Object startValue, Object endValue) {int startInt = (Integer) startValue;float startA = ((startInt >> 24) & 0xff) / 255.0f;float startR = ((startInt >> 16) & 0xff) / 255.0f;float startG = ((startInt >> 8) & 0xff) / 255.0f;float startB = (startInt & 0xff) / 255.0f;int endInt = (Integer) endValue;float endA = ((endInt >> 24) & 0xff) / 255.0f;float endR = ((endInt >> 16) & 0xff) / 255.0f;float endG = ((endInt >> 8) & 0xff) / 255.0f;float endB = (endInt & 0xff) / 255.0f;// convert from sRGB to linearstartR = (float) Math.pow(startR, 2.2);startG = (float) Math.pow(startG, 2.2);startB = (float) Math.pow(startB, 2.2);endR = (float) Math.pow(endR, 2.2);endG = (float) Math.pow(endG, 2.2);endB = (float) Math.pow(endB, 2.2);// compute the interpolated color in linear spacefloat a = startA + fraction * (endA - startA);float r = startR + fraction * (endR - startR);float g = startG + fraction * (endG - startG);float b = startB + fraction * (endB - startB);// convert back to sRGB in the [0..255] rangea = a * 255.0f;r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);}
}

看源码中怎么渐变颜色的:ArgbEvaluator.getInstance()
可以看到有个计算颜色的方法,拿来用呗(我直接拷过去用)

public static ValueAnimator ofArgb(int... values) {ValueAnimator anim = new ValueAnimator();anim.setIntValues(values);anim.setEvaluator(ArgbEvaluator.getInstance());return anim;
}---->[计算颜色方法evaluate]---------------
public Object evaluate(float fraction, Object startValue, Object endValue) {//计算颜色方法详情......
}

3.使用估值器指定曲线方程运动

该方程是二次曲线:y=x*x/800 当然你也可以定义自己喜欢的方程

指定曲线方程运动.gif

public class Ball {public int color;public int r;public int x;public int y;public Ball() {}public Ball(int r, int color) {this.color = color;this.r = r;}public Ball(int r, int color, int x, int y) {this.color = color;this.r = r;this.x = x;this.y = y;}
}

估值器修改:

public class BallEvaluator implements TypeEvaluator {@Overridepublic Object evaluate(float fraction, Object startValue, Object endValue) {Ball start = (Ball) startValue;Ball end = (Ball) endValue;Ball ball = new Ball();ball.color = evaluateColor(fraction, start.color, end.color);ball.r = (int) (start.r + fraction * (end.r - start.r));ball.x = (int) (start.x + fraction * (end.x - start.x));ball.y= ball.x*ball.x/800;//此处依赖x确定y值return ball;}
}

AnimatorView

public class AnimatorView extends View {private static final String TAG = "AnimatorView";private Paint mPaint;private int mRadius = 50;private int dx;private int dy;private ValueAnimator mAnimator;private ValueAnimator mColorAnimator;private ValueAnimator mObjAnimator;public AnimatorView(Context context) {this(context, null);}public AnimatorView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}private void init() {mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setColor(0xff94E1F7);Ball startBall = new Ball(50, 0xff94E1F7,0,0);Ball endBall = new Ball(100, 0xffF35519,500,1000);mObjAnimator = ValueAnimator.ofObject(new BallEvaluator(), startBall, endBall);mObjAnimator.setRepeatMode(ValueAnimator.REVERSE);//反转开始100->300 300->100mObjAnimator.setDuration(1000);//设置时长mObjAnimator.setRepeatCount(1);//设置重复执行次数mObjAnimator.setRepeatMode(ValueAnimator.REVERSE);mObjAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {Ball ball = (Ball) animation.getAnimatedValue();mRadius = ball.r;mPaint.setColor(ball.color);dx=ball.x;dy=ball.y;Log.e(TAG, "onAnimationUpdate: "+dx+":"+dy);invalidate();}});}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.translate(dx, dy);canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:mObjAnimator.start();break;case MotionEvent.ACTION_UP:}return super.onTouchEvent(event);}
}

基本套路就是这样,有了ofObject,属性随意变,还怕动画吗?
核心就是估值器的定义,其实ofInt,ofFloat,ofArgb只是适用了内置估值器而已
本质上和ofObject并没有什么不同,可以看成单属性的简易版ofObject


三、插值器

如果估值器TypeEvaluator告诉你给怎么跑,那么插值器则告诉你跑多快
下面演示一下三个内置插值器(内置还有几个,自己试试)和自定义的三个插值器

插值器.gif


1.自定义插值器:sin型先快后慢

这里的input是从0~1变化的值,插值器就是改变input值的变化情况

public class D_Sin_Inter implements TimeInterpolator {@Overridepublic float getInterpolation(float input) {//input是一个从0~1均匀变化的值//从0到PI/2均匀变化的值float rad = (float) (Math.PI/2 * input);//返回这个弧度的sin值--sin曲线在0~PI/2区域是增长越来越缓慢,小球运动越来越缓慢return (float) (Math.sin(rad));}
}

2.自定义插值器:sin型先满后快
public class A_Sin_Inter implements TimeInterpolator {@Overridepublic float getInterpolation(float input) {//input是一个从0~1均匀变化的值//从0到PI/2均匀变化的值float rad = (float) (Math.PI/2 * input+Math.PI/2);//返回这个弧度的sin值--sin曲线在PI/2~PI区域是降低越来越快return (float) (1-(Math.sin(rad)));//返回1-}
}

3.自定义插值器:log型
/*** 作者:张风捷特烈<br/>* 时间:2018/12/26 0026:20:41<br/>* 邮箱:1981462002@qq.com<br/>* 说明:Log型先快后慢*/
public class D_Log_Inter implements TimeInterpolator {@Overridepublic float getInterpolation(float input) {return (float) (Math.log10(1 + 9 * input));}
}

插值器实际上就是基于input加工,时间流动(每次刷新间隔)是基本恒定的,
input是从0~1均匀变化的,通过input将其映射到一组对应关系上,就像数学中的函数
input是x,称为自变量,因变量y由函数式和x确定,返回值便是y,供代码中使用(D_Sin_Inter如下)
LinearInterpolator线性插值器也就是x=y,而已,本质是一样的


4.优雅的实现测试代码

只需在名字数组和插值器数组里对应添加即可,其他会自动处理

public class AnimatorInterView extends View {private static final String TAG = "AnimatorView";private Paint mPaint;private int mRadius = 50;private int dx[];private String[] mStrings;private TimeInterpolator[] mInterpolators;public AnimatorInterView(Context context) {this(context, null);}public AnimatorInterView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}private void init() {mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setColor(0xff94E1F7);mPaint.setTextSize(40);mStrings = new String[]{"Linear", "Bounce", "AOI", "OI", "D_sin", "D_log", "A_sin", "A_log"};mInterpolators = new TimeInterpolator[]{new LinearInterpolator(),new BounceInterpolator(),new AnticipateOvershootInterpolator(),new OvershootInterpolator(),new D_Sin_Inter(),new D_Log_Inter(),new A_Sin_Inter()};dx = new int[mInterpolators.length];}private ValueAnimator createAnimator(int index, TimeInterpolator interpolator) {ValueAnimator mAnimator = ValueAnimator.ofInt(0, 800);mAnimator.setRepeatCount(1);//设置重复执行次数mAnimator.setRepeatMode(ValueAnimator.REVERSE);//反转开始100->300 300->100mAnimator.setDuration(3000);//设置时长mAnimator.setInterpolator(interpolator);mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {dx[index] = (int) animation.getAnimatedValue();invalidate();}});return mAnimator;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);for (int i = 0; i < dx.length; i++) {canvas.translate(0, 120);mPaint.setColor(0xff94E1F7);canvas.drawCircle(mRadius + dx[i], mRadius, mRadius, mPaint);mPaint.setColor(0xff000000);mPaint.setStrokeWidth(4);canvas.drawLine(mRadius, mRadius, 800 + mRadius, mRadius, mPaint);canvas.drawText(mStrings[i], 800 + 3 * mRadius, mRadius, mPaint);}}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:for (int i = 0; i < mInterpolators.length; i++) {createAnimator(i, mInterpolators[i]).start();}break;case MotionEvent.ACTION_UP:}return super.onTouchEvent(event);}
}

[插曲]:路径于Animator的结合

核心是使用PathMeasure和DashPathEffect对路径的长度进行控制
关于Path的这方面知识,这里不做详解,详见:Android关于Path你所知道的和不知道的一切

路径动画.gif

/*** 作者:张风捷特烈<br/>* 时间:2018/12/26 0026:7:50<br/>* 邮箱:1981462002@qq.com<br/>* 说明:Animator与Path*/
public class AnimatorPathView extends View {private static final String TAG = "AnimatorView";private Paint mPaint;private Path mPath;private PathMeasure pathMeasure;public AnimatorPathView(Context context) {this(context, null);}public AnimatorPathView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}private void init() {mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setColor(0xff94E1F7);mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeWidth(10);mPaint.setStrokeJoin(Paint.Join.ROUND);//测量路径mPath = new Path();mPath = nStarPath(mPath, 8, 250, 160);//八角形路径pathMeasure = new PathMeasure(mPath, false);}private ValueAnimator createAnimator() {ValueAnimator mAnimator = ValueAnimator.ofInt(0, 800);mAnimator.setRepeatCount(1);//设置重复执行次数mAnimator.setRepeatMode(ValueAnimator.REVERSE);//反转开始100->300 300->100mAnimator.setDuration(3000);//设置时长mAnimator.setInterpolator(new AnticipateOvershootInterpolator());mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float value = animation.getAnimatedFraction();//核心:创建DashPathEffectDashPathEffect effect = new DashPathEffect(new float[]{pathMeasure.getLength(),pathMeasure.getLength()},value * pathMeasure.getLength());mPaint.setPathEffect(effect);invalidate();}});return mAnimator;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.translate(250, 250);canvas.drawPath(mPath, mPaint);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:createAnimator().start();break;case MotionEvent.ACTION_UP:}return super.onTouchEvent(event);}/*** n角星路径** @param num 几角星* @param R   外接圆半径* @param r   内接圆半径* @return n角星路径*/public static Path nStarPath(Path path, int num, float R, float r) {float perDeg = 360 / num;float degA = perDeg / 2 / 2;float degB = 360 / (num - 1) / 2 - degA / 2 + degA;path.moveTo((float) (Math.cos(rad(degA)) * R), (float) (-Math.sin(rad(degA)) * R));for (int i = 0; i < num; i++) {path.lineTo((float) (Math.cos(rad(degA + perDeg * i)) * R),(float) (-Math.sin(rad(degA + perDeg * i)) * R));path.lineTo((float) (Math.cos(rad(degB + perDeg * i)) * r),(float) (-Math.sin(rad(degB + perDeg * i)) * r));}path.close();return path;}/*** 角度制化为弧度制** @param deg 角度* @return 弧度*/public static float rad(float deg) {return (float) (deg * Math.PI / 180);}
}

第二节:ValueAnimator之子ObjectAnimator和TimeAnimator

作为孩子,它老爸能做的它也能做,并且还会有一些自己的特长
ObjectAnimator针对有setXxx方法的属性,进行的"Xxx"属性变化动画
注:Xxx的首字母大小写都可以


一、View内置属性的测试

1.简单入门--下移示例:
下移动.gif

private ObjectAnimator mMoveDown;//下移动画
mMoveDown = ObjectAnimator//创建实例//(View,属性名,初始化值,结束值).ofFloat(this, "translationY", 0, 300).setDuration(1000);//设置时常
@Override//绘制方法
protected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawCircle(50, 50, 50, mPaint);
}
mMoveDown.start();//开启动画

加上背景看一下,可以看出是整个View进行了变化。

加背景.gif


2.常用属性一览:
属性名 演示 解释
alpha
透明度1~0
translationX
X方向移动
translationY
Y方向移动
rotation
旋转(默认View中心点)
rotationX
X轴旋转(默认View中心横轴)
rotationY
Y轴旋转(默认View中心纵轴)
scaleX
X缩放 倍数
scaleY
Y缩放 倍数

3.旋转、缩放中心点设置:
setPivotX(200);
setPivotY(200);
旋转中心点.gif


4.多参数情况(多参情况Animator家族皆适用)

0-->360 360-->0 0-->90

.ofFloat(this, "rotation", 0, 360,360,0,0,90)
多参数.gif


二、自定义ObjectAnimator属性

内置的只是一些常用的,我们也可以自定义自己的属性

1.自定义圆的大小动画

必须用一个setXxx的方法,属性名则为xxx,调用重绘方法

public void setRadius(int radius) {mRadius = radius;invalidate();//记得重绘
}
ObjectAnimator//创建实例//(View,属性名,初始化值,结束值).ofInt(this, "Radius", 100, 50,100,20,100).setDuration(3000);//设置时常
自定义半径.gif


2.自定义颜色动画
public void setColor(int color) {mColor = color;mPaint.setColor(mColor);invalidate();//记得重绘
}
colorAnimator = ObjectAnimator//创建实例//(View,属性名,初始化值,结束值).ofInt(this, "color", 0xff0000ff, 0xffF2BA38, 0xffDD70BC).setDuration(3000);
colorAnimator.setEvaluator(new ArgbEvaluator());//颜色的估值器
自定义颜色.gif


3.ValueAnimator和ObjectAnimator的区别在哪?
1.ValueAnimator需要手动添加监听,手动获取ValueAnimator的数据,手动书写变更逻辑
2.ObjectAnimator可以不用进行更新监听,核心在`setXxx`里进行,
也就是每次更新时会自己走setXxx里的方法,这样方便在外部使用来动态改变属性
3.ValueAnimator的灵活性要好,毕竟自己动手,可以脑洞大开,想怎么玩怎么玩
4.ObjectAnimator针对有setXxx的属性进行动画,两者的侧重点不同
5.总的来说ObjectAnimator向于应用(简洁,快速),ValueAnimator偏向于操作(灵活,多变)

三、TimeAnimator

这个类总共代码100行,而且几乎一半都是注释
它继承自ValueAnimator,可谓也是Animator家族的掌上明珠,但非常纯真与专注
她想做的只有一件事:提供一条时间流(每个16或17ms回调一次方法)

mAnimator = new TimeAnimator();
(自己,运行总时长,每次回调的时间间隔)
mAnimator.setTimeListener((animation, totalTime, deltaTime) -> {Log.e(TAG, "totalTime:" + totalTime + ",  deltaTime:" + deltaTime);if (totalTime > 300) {animation.pause();}
});

运行结果:

2018-12-27 10:09:35.047  E/TimeAnimatorView: totalTime:0,  deltaTime:0
2018-12-27 10:09:35.051  E/TimeAnimatorView: totalTime:2,  deltaTime:2
2018-12-27 10:09:35.068  E/TimeAnimatorView: totalTime:19,  deltaTime:17
2018-12-27 10:09:35.085  E/TimeAnimatorView: totalTime:36,  deltaTime:17
2018-12-27 10:09:35.101  E/TimeAnimatorView: totalTime:52,  deltaTime:16
2018-12-27 10:09:35.118  E/TimeAnimatorView: totalTime:69,  deltaTime:17
2018-12-27 10:09:35.135  E/TimeAnimatorView: totalTime:86,  deltaTime:17
2018-12-27 10:09:35.151  E/TimeAnimatorView: totalTime:102,  deltaTime:16
2018-12-27 10:09:35.167  E/TimeAnimatorView: totalTime:119,  deltaTime:17
2018-12-27 10:09:35.184  E/TimeAnimatorView: totalTime:136,  deltaTime:17
2018-12-27 10:09:35.200  E/TimeAnimatorView: totalTime:152,  deltaTime:16
2018-12-27 10:09:35.218  E/TimeAnimatorView: totalTime:169,  deltaTime:17
2018-12-27 10:09:35.234  E/TimeAnimatorView: totalTime:186,  deltaTime:17
2018-12-27 10:09:35.251  E/TimeAnimatorView: totalTime:202,  deltaTime:16
2018-12-27 10:09:35.268  E/TimeAnimatorView: totalTime:219,  deltaTime:17
2018-12-27 10:09:35.284  E/TimeAnimatorView: totalTime:236,  deltaTime:17
2018-12-27 10:09:35.300  E/TimeAnimatorView: totalTime:252,  deltaTime:16
2018-12-27 10:09:35.318  E/TimeAnimatorView: totalTime:269,  deltaTime:17
2018-12-27 10:09:35.334  E/TimeAnimatorView: totalTime:286,  deltaTime:17
2018-12-27 10:09:35.350  E/TimeAnimatorView: totalTime:303,  deltaTime:17

这样关于ValueAnimator基本上就结束了(还有几个监听,最后一起将)


四、AnimatorSet

综合前几次的动画效果,拼装在一起,AnimatorSet本身并不难

1.Builder模式的AnimatorSet

源码一翻,可见里面有个Builder,可就是建造者模式了,
每个动画在AnimatorSet中是一个Node,Budiler中的方法就是:
为处理当前节点和插入节点的关系,看下面一组动画 :

动画集合.gif

mSet//半径-->移动+渐变-->变色.play(translationX)//移动.with(alpha)//渐变.after(radiusAnimator)//半径.before(colorAnimator);//变色

测试源码:

public class AnimatorSetView extends View {private static final String TAG = "AnimatorView";private Paint mPaint;private int mRadius = 50;private int mColor = 50;private ObjectAnimator colorAnimator;private ObjectAnimator radiusAnimator;ObjectAnimator translationX;ObjectAnimator alpha;private AnimatorSet mSet;public AnimatorSetView(Context context) {this(context, null);}public AnimatorSetView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}private void init() {mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setColor(0xff94E1F7);mSet = new AnimatorSet();translationX = ObjectAnimator//创建实例//(View,属性名,初始化值,结束值).ofFloat(this, "translationX", 0, 300, 150, 100, 20, 100).setDuration(3000);//设置时常alpha = ObjectAnimator//创建实例//(View,属性名,初始化值,结束值).ofFloat(this, "alpha", 1, 0.5f, 1, 0, 1).setDuration(3000);//设置时常radiusAnimator = ObjectAnimator//创建实例//(View,属性名,初始化值,结束值).ofInt(this, "Radius", 50, 100, 50, 100, 20, 100).setDuration(3000);//设置时常colorAnimator = ObjectAnimator//创建实例//(View,属性名,初始化值,结束值).ofInt(this, "color", 0xff0000ff, 0xffF2BA38, 0xffDD70BC).setDuration(3000);colorAnimator.setEvaluator(new ArgbEvaluator());//颜色的估值器mSet//半径-->移动+渐变-->变色.play(translationX).with(alpha).after(radiusAnimator).before(colorAnimator);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:mSet.start();break;case MotionEvent.ACTION_UP:}return super.onTouchEvent(event);}public void setRadius(int radius) {mRadius = radius;setMeasuredDimension(mRadius * 2, mRadius * 2);invalidate();//记得重绘}public void setColor(int color) {mColor = color;mPaint.setColor(mColor);invalidate();//记得重绘}
}

2.AnimatorSet自身方法:

顾名思义:也就是一起运动还是分批运动

mSet.playTogether(translationX,alpha,radiusAnimator,colorAnimator);
mSet.playSequentially(translationX,alpha,radiusAnimator,colorAnimator);

四、Animator的监听:

可见Animator有两个内部接口,AnimatorListenerAnimatorPauseListener
AnimatorListenerAdapter是两个接口的空实现类,标准适配器模式。
ValueAnimator作为孩子,有自己的一个接口AnimatorUpdateListener

监听接口关系.png

1、AnimatorListener:动画监听

Animator中的监听器两个孩子也都能用

   //动画开启时回调void onAnimationStart(Animator animation);//动画结束时回调void onAnimationEnd(Animator animation);//动画取消时回调void onAnimationCancel(Animator animation);//重复时回调void onAnimationRepeat(Animator animation);

2.动画测试

开始时设为绿色-->重复时设为随机色-->取消是大小变为50-->结束时设为蓝色

动画监听.gif

mTranslationX = translationX();
mTranslationX.setRepeatMode(ValueAnimator.REVERSE);
mTranslationX.setRepeatCount(ValueAnimator.INFINITE);mTranslationX.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {//开始时设为绿色setColor(Color.GREEN);}@Overridepublic void onAnimationEnd(Animator animation) {//结束时设为蓝色setColor(Color.BLUE);}@Overridepublic void onAnimationCancel(Animator animation) {//取消时大小变为50setCircleR(50);}@Overridepublic void onAnimationRepeat(Animator animation) {//重复时设为随机色setColor(ColUtils.randomColor());}
});
mTranslationX.start();
 mTranslationX.cancel();//取消动画

3、AnimatorPauseListener:动画暂停监听
//暂停回调
void onAnimationPause(Animator animation);
//恢复回调
void onAnimationResume(Animator animation);

效果如下:点击运动,右滑暂停颜色变黄,下滑恢复颜色变蓝

暂停监听.gif

mTranslationX.addPauseListener(new Animator.AnimatorPauseListener() {@Overridepublic void onAnimationPause(Animator animation) {setColor(Color.YELLOW);//暂停黄色}@Overridepublic void onAnimationResume(Animator animation) {setColor(Color.BLUE);//恢复蓝色}
});

4、AnimatorUpdateListener: ValueAnimator一系专有监听
//更新时回调
void onAnimationUpdate(ValueAnimator animation);

效果如下:每当更新是将半径和位移联动

更新监听.gif

mTranslationX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mCircleR = (Float) animation.getAnimatedValue();invalidate();}
});

五、Animator家族在xml中的使用:

在res下创建:animator文件夹

1.Animator标签

直接用animator标签感觉也有点麻烦,这里看一下吧

xml中属性 含义 代码中对应
duration 播放的时长 setDuration()
valueType 参数值类型 ofXXX
valueFrom 初始值 ofXXX(第1参)
valueTo 结束值 ofXXX(第2参)
startOffset 延时 startDelay()
repeatCount 重复次数 setRepeatCount()
interpolator 插值器 setRepeatMode()

1.1.animator.xml
xml中使用.gif

<?xml version="1.0" encoding="utf-8"?>
<animatorxmlns:android="http://schemas.android.com/apk/res/android"android:duration="2000"android:repeatCount="2"android:repeatMode="reverse"android:startOffset="1000"android:valueFrom="0dp"android:valueType="floatType"android:valueTo="200dp">
</animator>

1.2.布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><Viewandroid:id="@+id/id_btn_go"android:layout_width="50dp"android:layout_height="50dp"android:layout_marginStart="24dp"android:layout_marginTop="32dp"android:background="#3ED7FA"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>

1.3.代码中使用:MainActivity

由Xml获取ValueAnimator,之后的事,就自己动手,感觉有点麻烦

View button = findViewById(R.id.id_btn_go);
ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.animator);animator.addUpdateListener(anim->{float animatedValue = (float) anim.getAnimatedValue();button.setTranslationX(animatedValue);
});button.setOnClickListener((v)->{animator.start();
});

2.setobjectAnimator标签

objectAnimator多了一个propertyName属性,其余一致


2.1set_obj_animator.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"android:ordering="sequentially"><objectAnimatorandroid:duration="1500"android:propertyName="rotationY"android:valueFrom="0"android:valueTo="180"/><objectAnimatorandroid:duration="1500"android:propertyName="alpha"android:valueFrom="0.3f"android:valueTo="1f"/><objectAnimatorandroid:duration="1500"android:propertyName="translationX"android:valueFrom="0"android:valueTo="180dp"/>
</set>

2.2:代码中使用
View button = findViewById(R.id.id_btn_go);
Animator set_obj = AnimatorInflater.loadAnimator(this, R.animator.set_obj_animator);
et_obj.setTarget(button);button.setOnClickListener((v)->{set_obj.start();
});

3、最后看一下我大objectAnimator变换路径

详情可见:Android资源res之矢量图完全指南(加SVG-path命令分析)

path.png

箭头:M8,50, l100,0 M0,47, l40,40 M0,52 l40 -40
菜单:M0,50, l80,0 M0,80, l80,0 M0,20 l80 0
path变形 变形+旋转

1.将两个path字符串放入string.xml

直接写也可以,但复用不方便

<resources><string name="app_name">test</string><string name="path_from">M8,50, l100,0 M0,47, l40,40 M0,52 l40 -40 </string><string name="path_to">M0,50, l80,0 M0,80, l80,0 M0,20 l80 0</string>
</resources>

2.矢量图:path_test.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"android:width="48dp"android:height="48dp"android:viewportWidth="100"android:viewportHeight="100"><groupandroid:translateX="4"android:translateY="4"><pathandroid:pathData="M0,0 A30,50,90,0,1,50,50"android:strokeWidth="4"android:strokeColor="@color/black"/></group>
</vector>

3.旋转动画:rotation_animator.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimatorxmlns:android="http://schemas.android.com/apk/res/android"android:duration="1000"android:propertyName="rotation"android:valueFrom="0"android:valueTo="180"/>

4.路径动画:path_animator.xml
<?xml version="1.0" encoding="utf-8"?><objectAnimatorxmlns:android="http://schemas.android.com/apk/res/android"android:duration="1000"android:interpolator="@android:interpolator/linear"android:propertyName="pathData"android:valueFrom="@string/path_from"android:valueTo="@string/path_to"android:valueType="pathType"/>

5.矢量图文件:icon_path.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"android:width="48dp"android:height="48dp"android:viewportWidth="100"android:viewportHeight="100"><group android:name="container"android:translateX="8"android:pivotX="50"android:scaleY="0.8"android:scaleX="0.8"android:pivotY="50"><pathandroid:name="alpha_anim"android:pathData="@string/path_from"android:strokeWidth="8"android:strokeColor="#000"/></group>
</vector>

6.整合动画:anima_path.xml
<animated-vectorxmlns:android="http://schemas.android.com/apk/res/android"android:drawable="@drawable/icon_path"><targetandroid:name="alpha_anim"android:animation="@animator/path_animator"/><targetandroid:name="container"android:animation="@animator/rotation_animator"></target>
</animated-vector>

7.使用动画:
 <ImageViewandroid:id="@+id/id_iv"android:layout_width="200dp"android:layout_height="200dp"android:src="@drawable/anima_path"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"/>
//点击时:
Drawable drawable = mIdIv.getDrawable();
if (drawable instanceof Animatable){((Animatable) drawable).start();
}

ok,这样就行了,你可以随意定制两个路径,但必须保证两个路径的指令相同,不然会崩


后记:捷文规范

1.本文成长记录及勘误表
项目源码 日期 备注
V0.1--github 2018-12-27 Android动画Animator家族使用指南
2.更多关于我
笔名 QQ 微信 爱好
张风捷特烈 1981462002 zdl1994328 语言
我的github 我的简书 我的掘金 个人网站
3.声明

1----本文由张风捷特烈原创,转载请注明
2----欢迎广大编程爱好者共同交流
3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
4----看到这里,我在此感谢你的喜欢与支持


icon_wx_200.png

Android动画Animator家族使用指南相关推荐

  1. Android 动画 Animator 家族使用指南

    零.前言:本文知识点 ValueAnimator的认识与使用 估值器TypeEvaluator的自定义与使用 插值器TimeInterpolator的自定义与使用 Path与Animator的结合使用 ...

  2. Android 动画 Animator 家族

    Animator 目录 思维导图 帧动画 使用方式 优缺点 应用场景 补间动画 位移.旋转.缩放.透明度动画 优缺点 应用场景 属性动画 层次关系 ValueAnimator ObjectAnimat ...

  3. animator android,Android动画Animator开发问题

    在开发Android的Animator时遇到如下问题: 1. 关于Animator的循环播放: 在Animator动画中有循环的设置方法setRepeatCount(ValueAnimator.INF ...

  4. android 属性动画实例,Android 属性动画Animator工具类代码案例

    代码分享-> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ packa ...

  5. Android Animator(Android动画)

    前言:又是很久没有写blog了,原本想看完<Android群英转>在来总结一下的,但是看到后面突然发现前面看过的东西都忘记了.突然发现还是要总结一下....一定要总结!一定要总结!因为我发 ...

  6. Android动画特效之Animator属性动画实现

    Android动画特效之自定义view: Android动画特效之自定义view_Angel-杭州的博客-CSDN博客_android view 设置动画 由于上期Android动画特效之自定义Vie ...

  7. Android动画了解—转场/过渡(Transition) 动画

    转场/过渡(Transition) 动画 Transition 是指不同 UI 状态转换时的动画. 界面过渡 界面 的 过渡 可以 分为 进入/出场 的过渡动画 设置进场/出场/返回/重新进入 的过渡 ...

  8. android jason动画,Android 动画之Lottie动画使用

    Android 动画之Lottie动画使用 一:简介 Lottie是Airbnb开源的一套跨平台的完整解决方案,设计师只需要使用After Effects(简称AE)设计动画之后,使用Lottic提供 ...

  9. android动画封装,Android属性动画封装,快速构建动画

    Android实现动画效果的方式主要有帧动画.补间动画.属性动画.关于安桌动画的基础知识可以查看这篇文章Android属性动画完全解析 这里我要讲的是如何快速构建出一个动画效果,如下图: 如果我们用属 ...

最新文章

  1. Starting MySQL....The server quit without updating PID file 处理方法
  2. 框架、文档、视图类之间的调用关系
  3. BorderContainer的圆角问题
  4. [OS复习]设备管理1
  5. 一个个人网站如何融资一千万
  6. React开发(167):...数组拼接
  7. 展示29个美丽的iPhone壁纸
  8. java Integer 源码学习
  9. celery 停止_celery 停止执行中 task
  10. 笔记-Handheld multi-frame super-resolution之一
  11. 专科python应届生工资多少-阿里员工吐槽:应届生工资太猛,被倒挂,后悔接阿里侮辱性offer...
  12. ORA-00119: invalid specification for system parameter LOCAL_LISTENER;
  13. 案例:回归分析-R实现
  14. 2014年5月第二个周末总结--保守自己的心
  15. java敏感词过滤_java敏感词过滤
  16. 关于花瓣网header条的思考
  17. iCloud和AppStore区别(为什么不让登陆iCloud)
  18. 服务器 虚拟声卡,虚拟声卡,小编教你怎么安装虚拟声卡
  19. 云服务器安装软件,如何在云服务器中安装软件
  20. 元学习入门:MAML

热门文章

  1. 惠普计划未来3年裁员2.46万人 年节省$18亿
  2. 学习SpringBoot 集成mybaties (参考纯洁的微笑) 记录其中遇到的问题
  3. python --- 短信接口开发手机验证码发送
  4. 【软件测试】测试人,生活不止眼前的测试,还有诗和远方......
  5. 【云计算】云计算架构师ACE成长路线
  6. 【面试题】2021年PHP高频面试题汇总
  7. 用wds方式完成无线路由桥接
  8. JavaScript控制input输入框的required属性值
  9. Docker 容器 jvm 内存参数调整优化
  10. 计算机 节能措施,电脑出现节能模式怎么办