零、前言:本文知识点

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

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


第一节:ValueAnimator的使用

一、简单的使用

0.Animator家族简单认识:

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


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刷新界面,并动态改变数值

/*** 作者:张风捷特烈<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 当然你也可以定义自己喜欢的方程

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告诉你给怎么跑,那么插值器则告诉你跑多快
下面演示一下三个内置插值器(内置还有几个,自己试试)和自定义的三个插值器


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你所知道的和不知道的一切

/*** 作者:张风捷特烈<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.简单入门--下移示例:
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进行了变化。


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);
复制代码

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

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

.ofFloat(this, "rotation", 0, 360,360,0,0,90)
复制代码

二、自定义ObjectAnimator属性

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

1.自定义圆的大小动画

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

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

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());//颜色的估值器
复制代码

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中的方法就是: 为处理当前节点和插入节点的关系,看下面一组动画 :

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有两个内部接口,AnimatorListenerAnimatorPauseListenerAnimatorListenerAdapter是两个接口的空实现类,标准适配器模式。
ValueAnimator作为孩子,有自己的一个接口AnimatorUpdateListener

1、AnimatorListener:动画监听

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

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

2.动画测试

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

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);
复制代码

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

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);
复制代码

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

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 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命令分析)

箭头: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----看到这里,我在此感谢你的喜欢与支持


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. 语音文件转成文字怎么转
  2. 华为mate20 android,华为MateRS对比华为mate20RS,终于让安卓手机坐上了头等舱!
  3. vscode 取消拉去变基_GIT快速回忆
  4. python全栈开发笔记---------数据类型-----集合set
  5. 数据文件坏删除数据文件
  6. 最简单的基于FFmpeg的移动端例子附件:IOS自带播放器
  7. 使用Telnet客户端测试Exchange邮件收发
  8. Excel 哪个版本的好用?
  9. 【开发工具】【i2c-tools】I2C总线调试工具(i2c-tools)的安装与使用
  10. java Apache poi 操作word生成word目录(根据word模板生成word文件)
  11. 在linux目录中设置权限设置,linux下为目录和文件设置权限(示例代码)
  12. 自考-计算机程序设计-1-概论
  13. dell服务器T420装系统,ThinkPad T420笔记本一键u盘装系统win7教程
  14. Zabbix系列:设置动态监控告警时间范围
  15. pythoninstaller打包 其他电脑无法运行_Pyininstaller一个软件包故障坑,Pyinstaller,打包,失败,的...
  16. cardinality mysql_Cardinality统计取值不准确导致MYSQL选错索引
  17. GIV@2025:当一滴水流入螺旋世界,每个人都能化身超级英雄
  18. vs中readfile的作用_VC 中 ReadFile 和 WriteFile 函数读写串口超时机制
  19. 【办公类-22-02】周计划系列(2)-生成“主题知识”(提取旧docx指定段落的内容,写入EXCLE模板,再次生成新docx)
  20. mysql 去掉小数点后的多余的0

热门文章

  1. Redis哨兵集群搭建
  2. 火狐浏览器hackbar安装使用教程
  3. 五年级计算机学什么程序,小学五年级下册信息技术教案
  4. 数据库模式定义与基本表定义
  5. 微软CEO鲍尔默:WP8标志微软全面进军智能手机业务
  6. 说说 Spring 支持的 AspectJ 切点函数
  7. sap 内存参数调整
  8. GWAS理论 1-5 全基因组关联分析结果解读与经典案例介绍
  9. 【Sourcetree】推送远程仓库时,登录账户确认无误却一直失败的原因
  10. JavaScript的函数表达式