谨以文章记录学习历程,如有错误还请指明。
Google大大对动画的总述如下:
Animations can add visual cues that notify users about what’s going on in your app. They are especially useful when the UI changes state, such as when new content loads or new actions become available. Animations also add a polished look to your app, which gives it a higher quality look and feel.
没错,放上原文我只是装个逼,~
简单来说,动画就两个作用:
Java类名 | XML关键字 | 说明 |
---|---|---|
ValueAnimator | 放置在res/animator/目录下 |
在一个特定的时间里执行一个动画 |
TimeAnimator | 无 | 时序监听回调工具 |
ObjectAnimator | 放置在res/animator/目录下 |
一个对象的一个属性动画 |
AnimatorSet | 放置在res/animator/目录下 |
动画集合 |
指定时间内,修改属性(对象中对应的字段)的值,以此实现该对象在属性上的动画效果。
为了更好的理解,我们举一个栗子:
图1中我们搞出了一个假象的对象,动画作用于这个对象(实际可以说是对象的x属性,也即对象的水平位置),动画持续40ms,移动距离40px。每10ms(默认刷新速率),对象水平移动10px。40ms后,动画结束,物体停止在x=40处。这是一个典型的设置了linearInterpolator(匀速插值器)的动画。
为了更好的了解属性动画的工作原理,下面我们来看一看属性动画的组件是怎么计算上面例子的动画的。
其逻辑可以总结如下:
1.为 ValueAnimator 设置动画的时长,以及对应属性的始 & 末值
2.设置属性在 始 & 末值 间的变化逻辑
3.根据2中的逻辑更新当前值
4.获取3中更新的 值 ,修改 目标属性值
5.刷新视图。
6.重复4-5,直到 属性值 == 末值
下面给出动画工作的关键类
Java类 | 说明 |
---|---|
ValueAnimator | 动画执行类;核心 |
ObjectAnimator | 动画执行类 |
TimeInterpolator | 时间插值(插值器接口),控制动画变化率 |
TypeEvaluator | 类型估值(估值器接口),设置属性值计算方式,根据属性的 始 & 末值 和 插值 一起计算出当前时间的属性值 |
AnimatorSet | 动画集 |
AnimatorInflater | 加载属性动画的XML文件 |
一些额外的类
Java类 | 说明 |
---|---|
LayoutTransition | 布局动画,为布局的容器设置动画 |
ViewPropertyAnimator | 为View的动画操作提供一种更加便捷的用法 |
PropertyValuesHolder | 保存动画过程中所需要操作的属性和对应的值 |
Keyframe | 控制每个时间段执行的动画距离 |
AnimationListener AnimationUpdateListener AnimatorListenerAdapter |
动画事件的监听 |
对于控制的 值 的不同,Android 提供给我们三种构造方法来实例ValueAnimator对象
1. ValueAnimator.ofInt(int… values) – 整型数值
2. ValueAnimator.ofFloat(float… values) – 浮点型数值
3. ValueAnimator.ofObject(TypeEvaluator evaluator, Object… values) – 自定义对象类型
下面我们一一介绍
IntEvaluator
估值器方式1: Java 方式
推荐 Java 方式,因为某些时候我们需要动态获取属性的起始值,显然XML方式是不支持动态获取的。
//设置动画 始 & 末值
//ofInt()两个作用:
//1. 获取实例
//2. 在传入参数之间平滑过渡
//如下则0平滑过渡到3
ValueAnimator animator = ValueAnimator.ofInt(0,3);
//如下传入多个参数,效果则为0->5,5->3,3->10
//ValueAnimator animator = ValueAnimator.ofInt(0,5,3,10);
//设置动画的基础属性
animator.setDuration(5000);//播放时长
animator.setStartDelay(300);//延迟播放
animator.setRepeatCount(0);//重放次数
animator.setRepeatMode(ValueAnimator.RESTART);
//重放模式
//ValueAnimator.START:正序
//ValueAnimator.REVERSE:倒序
//设置更新监听
//值 改变一次,该方法就执行一次
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//获取改变后的值
int currentValue = (int) animation.getAnimatedValue();
//输出改变后的值
Log.d("1111", "onAnimationUpdate: " + currentValue);
//改变后的值发赋值给对象的属性值
view.setproperty(currentValue);
//刷新视图
view.requestLayout();
}
});
//启动动画
animator.start();
以上就是一个标准的Java方式的模板
方式2: XML 方式
1.在路径 res/animator/
路径下常见 XML 文件,如 set_animator.xml
2.在上述文件中设置动画参数
// ValueAnimator采用<animator> 标签
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:repeatCount="1"
android:repeatMode="reverse"/>
/>
3.Java代码启动动画
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.set_animation);
// 载入XML动画
animator.setTarget(view);
// 设置动画对象
animator.start();
// 启动动画
FloatEvaluator
估值器ofInt()
方式1:Java方式
ValueAnimator anim = ValueAnimator.ofFloat(0, 3);
//只是改了实例方法,除此之外完全一样
方式2:XML方式
只在设置动画 XML 文件中的属性时略有不同
// ValueAnimator 采用 标签
// ObjectAnimator 采用 标签
<animatorxmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:valueTo="200"
android:valueType="floatType"
android:propertyName="y"
android:repeatCount="1"
android:repeatMode="reverse"/>
// 创建初始动画的对象 & 结束动画的对象
Point point1 = new Point ();
Point point2 = new Point ();
// 创建动画对象 & 设置参数
ValueAnimator anim = ValueAnimator.ofObject(new myObjectEvaluator(), point1 , point2 );
// 参数说明
// 1. 自定义的估值器对象(TypeEvaluator 类型参数) - 下面会详细介绍
// 2. 初始动画的对象
// 3. 结束动画的对象
anim.setDuration(length);
anim.start();
上面示例中,我们看到有两个Point对象point1 , point2 。假设我们有一个自定义View,这个View中有一个Point对象用于管理坐标,然后我们在onDraw()方法中根据这个Point对象的坐标值进行绘制,也就是说,我们可以对Point对象进行动画操作,不停的根据Point的坐标刷新View重绘制,以此就可以实现 View 的动画了。
到这里都很好理解,不过我们还注意到传入了一个 new myObjectEvaluator()
(TypeEvaluator
实现类) 参数,这是干什么的呢?不要急,下面我们就详细解答。
其实我们已经不止一次的提到估值器的概念,如 ValueAnimator.ofFloat()
方法中的 IntEvaluator
, ValueAnimator.ofFloat()
方法中的 FloatEvaluator
,这两种都是Android预置供我们使用的,二者通过计算告知动画系统如何从初始值过渡到结束值。
但是二者虽然好用,但也有其局限性:只能针对 Int / Float 类型数值操作。因此某些我们需要对任意对象进行动画操作的时候,二者显然不能满足我们的需求了,这时候我们需要自定义一个TypeEvaluator来告知系统如何进行过渡。
那么如何自定义呢?别急,我们可以先看一看系统提供的 IntEvaluator
是如何实现的:
/**
* This evaluator can be used to perform type interpolation between int values.
*/
public class IntEvaluator implements TypeEvaluator {
/**
* This function returns the result of linearly interpolating the start and end values, with
* fraction representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: result = x0 + t * (v1 - v0),
* where x0 is startValue, x1 is endValue, and t is fraction.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value; should be of type int or Integer
* @param endValue The end value; should be of type int or Integer
* @return A linear interpolation between the start and end values, given the fraction parameter.
*/
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
源码很简单,注释也相当详细,方便观看,我再次解释一下:
IntEvaluator
实现了 TypeEvaluator
接口,然后重写了 evaluate()
方法。该方法的三个参数意义如下:
那么 evalute()
方法的返回值就不难理解了,简单的数学公式,返回当前动画的值。
是不是很简单?因此我们自定义 TypeEvaluator
时只需要实现 TypeEvaluator
接口,然后重写 evaluate()
方法,在此方法中处理好逻辑即可。
下面我们就动手写一个自定义 TypeEvaluator
我们还是以上面提过的Point对象管理View坐标的为例:
1.定义Point
类
public class Point {
//记录坐标位置
private float x;
private float y;
//通过构造方法设置坐标,因此不需要额外的set方法
public Point(float x, float y) {
this.x = x;
this.y = y;
}
//get方法,获取当前坐标值
public float getX() {
return x;
}
public float getY() {
return y;
}
}
2.定义 PointEvaluator
//实现TypeEvaluator接口
public class PointEvaluator implements TypeEvaluator {
//重写evaluate()方法
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
//始末值强转为Point对象
Point startPoint = (Point) startValue;
Point endPoint = (Point) endValue;
//通过fraction计算当前动画的坐标值x,y
float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
//返回以上述x,y组装的新的Point对象
Point point = new Point(x,y);
return point;
}
}
3.Point
对象间的平滑过渡
// 创建初始动画的对象 & 结束动画的对象
Point point1 = new Point(0, 0);
Point point2 = new Point(500, 500);
// 创建动画对象 & 设置参数
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), point1 , point2 );
anim.setDuration(3000);
anim.start();
以上就是自定义TypeEvaluator的全部用法。
下面我们就可以尝试用上述知识练习如何对对象进行动画操作,从而实现自定义View的动画效果
1.新建MyAnimView继承View
public class MyAnimView extends View {
//常量
public static final float RADIUS = 50f;
//当前Point,记录当前动画的值(x,y坐标)
private Point curPoint;
//画笔
private Paint mPaint;
//Java代码实例化View时调用
public MyAnimView(Context context) {
super(context);
}
//XML文件实例时调用
public MyAnimView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//初始化画笔
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLUE);
}
@Override
protected void onDraw(Canvas canvas) {
//第一次绘制时
if (curPoint == null){
//初始化坐标为(50f, 50f)
curPoint = new Point(RADIUS,RADIUS);
//画圆
drawCircle(canvas);
//开始动画
startAnimation();
}else {//非第一次绘制
drawCircle(canvas);
}
}
//在当前坐标处绘制一个半径为50f的圆
private void drawCircle(Canvas canvas) {
float x = curPoint.getX();
float y = curPoint.getY();
canvas.drawCircle(x,y,RADIUS,mPaint);
}
//开始动画
private void startAnimation() {
//设置 起始值&结束值
Point startPoint = new Point(RADIUS,RADIUS);//起始为左上角(50f,50f)
Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS);//终点为右下角(屏幕宽度-50f,屏幕高度-50f)
final ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(),startPoint,endPoint);
//设置插值器
anim.setInterpolator(new BounceInterpolator());
//设置监听
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
//每当Point的值有改变的时候,都会调用onAnimationUpdate()方法
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//更新curPoint,即更新当前坐标
curPoint = (Point) animation.getAnimatedValue();
// 刷新,重现调用onDraw()方法
// 由于curPoint的值改变,那么绘制的位置也会改变,也就实现了一个从左上到右下的平移动画
invalidate();
}
});
anim.setDuration(5000);
anim.start();
}
}
2.布局文件中引入该自定义view
"http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.whdalive.learn_propertyanimation.Property.MyAnimView
android:layout_width="match_parent"
android:layout_height="match_parent" />
上图中我们发现球并非匀速,而是有一个bounce的效果,这是因为我们为动画添加了一个插值器 anim.setInterpolator(new BounceInterpolator())
。
具体Interpolator的使用,我们在前一篇Android 视图动画(View Animation) 使用详解中已经详细讲解了,此处就不再赘述。
注意
ValueAnimator.ofObject()的本质还是操作 值, 只是将 多个值 封装到一个对象里,同时对 该对象 里的 多个值 一起操作而已
至此,ValueAnimator
的用法就介绍完了,下面我们介绍一个貌似更加常用的类 ObjectAnimator
由于继承关系的存在,ObjectAnimator的用法和ValueAnimator及其类似:Java方式设置 / XML方式设置(推荐Java方式)
方式1:Java方式
ObjectAnimator animator = ObjectAnimator.ofFloat(Object object, String property, float ....values);
//ObjectAnimator animator = ObjectAnimator.ofInt(Object object, String property, int ....values);
//ObjectAnimator animator = ObjectAnimator.ofObject(Object object, String property, TypeEvaluator evaluator,Object....values);
// 以ofFloat为例 参数说明:
// Object object:需要操作的对象
// String property:需要操作的对象的属性
// float ....values:动画初始值 & 结束值(不固定长度)
// 若是两个参数a,b,则动画效果则是从属性的a值到b值
// 若是三个参数a,b,c,则则动画效果则是从属性的a值到b值再到c值
// 以此类推
// 至于如何从初始值 过渡到 结束值,同样是由估值器决定,此处ObjectAnimator.ofFloat()是有系统内置的浮点型估值器FloatEvaluator,同ValueAnimator讲解
//动画基本属性
anim.setDuration(500);
anim.setStartDelay(500);
anim.setRepeatCount(0);
anim.setRepeatMode(ValueAnimator.RESTART);
animator.start();
// 启动动画
方式2:XML方式设置
1.路径res/animator/下创建动画XML文件,如set_animation.xml
2.设置动画参数
ObjectAnimator 采用 标签
"http://schemas.android.com/apk/res/android"
android:valueFrom="1" // 初始值
android:valueTo="0" // 结束值
android:valueType="floatType" // 变化值类型 :floatType & intType
android:propertyName="alpha" // 对象变化的属性名称
/>
3.Java启动动画
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.view_animation);
// 载入XML动画
animator.setTarget(view);
// 设置动画对象
animator.start();
// 启动动画
以上为ObjectAnimator的标准使用模板,我们通过控制传入 ofFloat()的第二个参数preperty 或者 XML中的android:propertyName属性 来产生不同动画效果,以下为Android预置好的一些属性:即四种基本变换,透明度、平移、缩放、旋转
属性 | 作用 | 数值类型 |
---|---|---|
alpha | 透明度 | float |
translationX | X方向的位移 | float |
translationY | Y方向的位移 | float |
scaleX | X方向的缩放倍数 | float |
scaleY | Y方向的缩放倍数 | float |
rotation | 以屏幕方向为轴的旋转度数 | float |
rotationX | 以X轴为轴的旋转度数 | float |
rotationY | 以Y轴为轴的旋转度数 | float |
那么问题来了,除了以上的属性,我们还可以传入哪些属性值呢?
答案是 任意属性值
为了理解这个问题,我们需要先了解一下ObjectAnimator是如何自动为属性赋值的。
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha",1f, 0f, 1f);
// TextView对象中并没有alpha这个属性值
// ObjectAnimator并不是直接对我们传入的属性名进行操作
// 而是根据传入的属性值"alpha" 去寻找对象对应属性名对应的get和set方法,从而通过set() & get()对属性进行赋值
// 因为TextView对象中有alpha属性所对应的get & set方法
// 所以传入的alpha属性是有效的
// 所以才能对alpha这个属性进行操作赋值
public void setRotation(float value);
public float getRotation();
// 实际上,这两个方法是由View对象提供的,其余平移旋转等属性同理
因此:
特别注意:如果想让对象的属性a的动画生效,属性a需要同时满足下面两个条件:
1. 对象必须要提供属性a的set()方法
a. 如果没传递初始值,那么需要提供get()方法,因为系统要去拿属性a的初始值
b. 若该条件不满足,程序直接崩溃
2. 对象提供的 属性a的set()方法 对 属性a的改变 必须通过某种方法反映出来
a. 如带来ui上的变化
b. 若这条不满足,动画无效,但不会崩溃)
本质:
下面我们通过实例来介绍如何通过自定义属性实现动画效果
public class MyAnimView extends View {
...
//将颜色设置为String类型,使用#RRGGBB格式表示颜色
private String color;
//get()方法
public String getColor() {
return color;
}
//set()方法
public void setColor(String color) {
mPaint.setColor(Color.parseColor(color));
this.color = color;
//改变画笔颜色后立即刷新视图,然后onDraw()方法就会调用
invalidate();
}
...
}
2.布局中加入自定义view
同前面ValueAnimator的自定义view
3.根据需求实现TypeEvaluator接口
public class ColorEvaluator implements TypeEvaluator {
// 实现TypeEvaluator接口
private int mCurrentRed;
private int mCurrentGreen ;
private int mCurrentBlue ;
// 复写evaluate()
// 在evaluate()里写入对象动画过渡的逻辑:此处是写颜色过渡的逻辑
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 获取到颜色的初始值和结束值
String startColor = (String) startValue;
String endColor = (String) endValue;
// 通过字符串截取的方式将初始化颜色分为RGB三个部分,并将RGB的值转换成十进制数字
// 那么每个颜色的取值范围就是0-255
int startRed = Integer.parseInt(startColor.substring(1, 3), 16);
int startGreen = Integer.parseInt(startColor.substring(3, 5), 16);
int startBlue = Integer.parseInt(startColor.substring(5, 7), 16);
int endRed = Integer.parseInt(endColor.substring(1, 3), 16);
int endGreen = Integer.parseInt(endColor.substring(3, 5), 16);
int endBlue = Integer.parseInt(endColor.substring(5, 7), 16);
// 将初始化颜色的值定义为当前需要操作的颜色值
mCurrentRed = startRed;
mCurrentGreen = startGreen;
mCurrentBlue = startBlue;
// 计算初始颜色和结束颜色之间的差值
// 该差值决定着颜色变化的快慢:初始颜色值和结束颜色值很相近,那么颜色变化就会比较缓慢;否则,变化则很快
// 具体如何根据差值来决定颜色变化快慢的逻辑写在getCurrentColor()里.
int redDiff = Math.abs(startRed - endRed);
int greenDiff = Math.abs(startGreen - endGreen);
int blueDiff = Math.abs(startBlue - endBlue);
int colorDiff = redDiff + greenDiff + blueDiff;
if (mCurrentRed != endRed) {
mCurrentRed = getCurrentColor(startRed, endRed, colorDiff, 0,
fraction);
// getCurrentColor()决定如何根据差值来决定颜色变化的快慢 ->>关注1
} else if (mCurrentGreen != endGreen) {
mCurrentGreen = getCurrentColor(startGreen, endGreen, colorDiff,
redDiff, fraction);
} else if (mCurrentBlue != endBlue) {
mCurrentBlue = getCurrentColor(startBlue, endBlue, colorDiff,
redDiff + greenDiff, fraction);
}
// 将计算出的当前颜色的值组装返回
String currentColor = "#" + getHexString(mCurrentRed)
+ getHexString(mCurrentGreen) + getHexString(mCurrentBlue);
// 由于我们计算出的颜色是十进制数字,所以需要转换成十六进制字符串:调用getHexString()->>关注2
// 最终将RGB颜色拼装起来,并作为最终的结果返回
return currentColor;
}
// 关注1:getCurrentColor()
// 具体是根据fraction值来计算当前的颜色。
private int getCurrentColor(int startColor, int endColor, int colorDiff,
int offset, float fraction) {
int currentColor;
if (startColor > endColor) {
currentColor = (int) (startColor - (fraction * colorDiff - offset));
if (currentColor < endColor) {
currentColor = endColor;
}
} else {
currentColor = (int) (startColor + (fraction * colorDiff - offset));
if (currentColor > endColor) {
currentColor = endColor;
}
}
return currentColor;
}
// 关注2:将10进制颜色值转换成16进制。
private String getHexString(int value) {
String hexString = Integer.toHexString(value);
if (hexString.length() == 1) {
hexString = "0" + hexString;
}
return hexString;
}
}
4.调用ObjectAnimator.ofObject()方法
//借助AnimatorSet,实现组合动画
public class MyAnimView extends View {
...
//开始动画
private void startAnimation() {
//设置 起始值&结束值
Point startPoint = new Point(RADIUS,RADIUS);//起始为左上角(50f,50f)
Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS);//终点为右下角(屏幕宽度-50f,屏幕高度-50f)
final ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(),startPoint,endPoint);
//设置插值器
anim.setInterpolator(new BounceInterpolator());
//设置监听
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
//每当Point的值有改变的时候,都会调用onAnimationUpdate()方法
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//更新curPoint,即更新当前坐标
curPoint = (Point) animation.getAnimatedValue();
// 刷新,重现调用onDraw()方法
// 由于curPoint的值改变,那么绘制的位置也会改变,也就实现了一个从左上到右下的平移动画
invalidate();
}
});
// anim.setDuration(5000);
//anim.start();
//颜色过渡的代码逻辑放在startAnimation()方法中,本身就在MyAnimView中执行,因此传入 this 参数即可
ObjectAnimator anim2 = ObjectAnimator.ofObject(this,"color",new ColorEvaluator(),"#FF0000", "#0000FF");
//创建一个AnimatorSet,让两个动画同时播放,时长5s
AnimatorSet animationSet = new AnimatorSet();
animationSet.play(anim).with(anim2);
animationSet.setDuration(5000);
//启动动画
animationSet.start();
}
以上我们就实现了一个平移 & 颜色变化的动画。
至此关于ValueAnimator和ObjectAnimeato的使用就讲解完了,下面我们再讲讲一些额外的使用方法
AnimatorSet.play(Animator anim) :播放当前动画
AnimatorSet.after(long delay) :将现有动画延迟x毫秒后执行
AnimatorSet.with(Animator anim) :将现有动画和传入的动画同时执行
AnimatorSet.after(Animator anim) :将现有动画插入到传入的动画之后执行
AnimatorSet.before(Animator anim) : 将现有动画插入到传入的动画之前执行
ObjectAnimator a1 = ObjectAnimator.ofFloat(view, "alpha", 1.0f, 0f);
ObjectAnimator a2 = ObjectAnimator.ofFloat(view, "translationY", 0f, viewWidth);
......
AnimatorSet animSet = new AnimatorSet();
animSet.setDuration(5000);
animSet.setInterpolator(new LinearInterpolator());
//animSet.playTogether(a1, a2, ...); //两个动画同时执行
animSet.play(a1).after(a2); //先后执行
......//其他组合方式
animSet.start();
XML方式
"together" | "sequentially"]>
"string"
android:duration="int"
android:valueFrom="float | int | color"
android:valueTo="float | int | color"
android:startOffset="int"
android:repeatCount="int"
android:repeatMode=["repeat" | "reverse"]
android:valueType=["intType" | "floatType"]/>
"int"
android:valueFrom="float | int | color"
android:valueTo="float | int | color"
android:startOffset="int"
android:repeatCount="int"
android:repeatMode=["repeat" | "reverse"]
android:valueType=["intType" | "floatType"]/>
...
anim.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animation animation) {
//动画开始时执行
}
@Override
public void onAnimationRepeat(Animation animation) {
//动画重复时执行
}
@Override
public void onAnimationCancel()(Animation animation) {
//动画取消时执行
}
@Override
public void onAnimationEnd(Animation animation) {
//动画结束时执行
}
});
// 特别注意:每次监听必须4个方法都重写。
anim.addListener(new AnimatorListenerAdapter() {
// 向addListener()方法中传入适配器对象AnimatorListenerAdapter()
// 由于AnimatorListenerAdapter中已经实现好每个接口
// 所以这里不实现全部方法也不会报错
@Override
public void onAnimationStart(Animator animation) {
// 如想只想监听动画开始时刻,就只需要单独重写该方法就可以
}
});
// 使用解析
View.animate().xxx().xxx();
// ViewPropertyAnimator的功能建立在animate()上
// 调用animate()方法返回值是一个ViewPropertyAnimator对象,之后的调用的所有方法都是通过该实例完成
// 调用该实例的各种方法来实现动画效果
// ViewPropertyAnimator所有接口方法都使用连缀语法来设计,每个方法的返回值都是它自身的实例
// 因此调用完一个方法后可直接连缀调用另一方法,即可通过一行代码就完成所有动画效果
// 以下是例子
mButton = (Button) findViewById(R.id.Button);
// 创建动画作用对象:此处以Button为例
mButton.animate().alpha(0f);
// 单个动画设置:将按钮变成透明状态
mButton.animate().alpha(0f).setDuration(5000).setInterpolator(new BounceInterpolator());
// 单个动画效果设置 & 参数设置
mButton.animate().alpha(0f).x(500).y(500);
// 组合动画:将按钮变成透明状态再移动到(500,500)处
// 特别注意:
// 动画自动启动,无需调用start()方法.因为新的接口中使用了隐式启动动画的功能,只要我们将动画定义完成后,动画就会自动启动
// 该机制对于组合动画也同样有效,只要不断地连缀新的方法,那么动画就不会立刻执行,等到所有在ViewPropertyAnimator上设置的方法都执行完毕后,动画就会自动启动
// 如果不想使用这一默认机制,也可以显式地调用start()方法来启动动画
至此,关于属性动画能想到的就全都讲解完了,至于有所遗漏的,留作日后再补充吧~