镇楼导图
1.帧动画
就是将一张张图片连贯起来播放,使用简单,但是要注意资源图片对内存的占用。
1.1示例
//使用
ImageView iv_start = findViewById(R.id.iv_start);
iv_start.setImageResource(R.drawable.jd_loading);
AnimationDrawable animationDrawable = (AnimationDrawable) iv_start.getDrawable();
animationDrawable.start();
2 补间动画
共有四种, alpha(淡入淡出),translate(位移),scale(缩放大小),rotate(旋转)。
两种使用方式,xml方式和代码方式
可组合嵌套使用,但行为总有局限
动画本质为影像的绘制,对View本身属性并无改变
2.1 xml使用示例
//单一使用
//组合使用
...
//代码操作
Animation animation = AnimationUtils.loadAnimation(this,R.anim.alpha_anim);
iv_start.startAnimation(animation);
pivotX 属性说明
决定了当前动画执行的参考位置
取值及含义如下
10--距离动画所在view自身左边缘10像素
10%--距离动画所在view自身左边缘 的距离是整个view宽度的10%
10%p--距离动画所在view父控件左边缘的距离是整个view宽度的10%
2.2 java code 示例
Animation rotateAnimation = new RotateAnimation(20,300,0.5f,0.5f);
Animation alphaAnimation = new AlphaAnimation(0,1);
Animation translateAnimation = new TranslateAnimation(0,10,0,10);
Animation scaleAnimation = new ScaleAnimation(0,10,0,10,0.5f,0.5f);
AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(rotateAnimation);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(translateAnimation);
animationSet.addAnimation(scaleAnimation);
animationSet.setFillAfter(true);//动画结束时是否保持最终状态
animationSet.setRepeatMode(Animation.RESTART);//重复方式
animationSet.setRepeatCount(-1);//重复次数
animationSet.setDuration(500);
iv_start.startAnimation(animationSet);
3 属性动画
3.1 来龙去脉
(1)帧动画和补间动画操作对象仅限于View
(2)帧动画如果图片资源太多较耗内存
(3)补间动画实现的效果仅限于alpha scale translate rotate
(4)补间动画并没有真正的改变属性,例如点击事件不会随位移而变化
(5)为了操作灵活,更好的扩展3.0之后引入属性动画
3.2 属性动画描述
(1)作用对象为java对象,不再仅限于View
(2)操作效果可扩展到任意具有get set方法的属性
(3)属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等。
(4)动画的使用不当可能会引发内存泄露,需要在onStop中取消动画
3.3 ValueAnimator
属性动画的核心类,通过不断控制值的变化,使用时需要手动将变化的值赋值给对象的属性。
常用API
ValueAnimator.ofInt(int values)对应估值器 IntEvaluator
ValueAnimator.ofFloat(float values)对应估值器FloatEvaluator
ValueAnimator.ofObject(int values)需要自己继承TypeEvaluator
3.3.1 java code 示例
//感谢https://www.jianshu.com/p/2412d00a0ce4提供的示例代码,下同
// 步骤1:设置动画属性的初始值 & 结束值
ValueAnimator anim = ValueAnimator.ofInt(0, 3);
// ofInt()作用有两个
// 1. 创建动画实例
// 2. 将传入的多个Int参数进行平滑过渡:此处传入0和1,表示将值从0平滑过渡到1
// 如果传入了3个Int参数 a,b,c ,则是先从a平滑过渡到b,再从b平滑过渡到C,以此类推
// ValueAnimator.ofInt()内置了整型估值器,直接采用默认的.不需要设置,即默认设置了如何从初始值 过渡到 结束值
// 下面看看ofInt()的源码分析 ->>关注1
// 步骤2:设置动画的播放各种属性
anim.setDuration(500);
// 设置动画运行的时长
anim.setStartDelay(500);
// 设置动画延迟播放时间
anim.setRepeatCount(0);
// 设置动画重复播放次数 = 重放次数+1
// 动画播放次数 = infinite时,动画无限重复
anim.setRepeatMode(ValueAnimator.RESTART);
// 设置重复播放动画模式
// ValueAnimator.RESTART(默认):正序重放
// ValueAnimator.REVERSE:倒序回放
// 步骤3:将改变的值手动赋值给对象的属性值:通过动画的更新监听器
// 设置 值的更新监听器
// 即:值每次改变、变化一次,该方法就会被调用一次
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 获得改变后的值
int currentValue = (Integer) animation.getAnimatedValue();
// 改变属性
View.setproperty(currentValue);
// 刷新视图,即重新绘制,从而实现动画效果
View.requestLayout();
}
});
// 启动动画
anim.start();
}
3.3.2 xml 示例
步骤1:在路径 res/animator的文件夹里创建相应的动画 .xml文件
步骤2:设置动画参数
// ValueAnimator采用 标签
步骤3:在Java代码中启动动画
// 载入XML动画
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.set_animation);
// 设置动画对象
animator.setTarget(view);
//手动改变属性
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator) {
// 获得每次变化后的属性值
int currentValue = (Integer) animator.getAnimatedValue();
// 每次值变化时,将值手动赋值给对象的属性
// 即将每次变化后的值 赋 给按钮的宽度,这样就实现了按钮宽度属性的动态变化
mButton.getLayoutParams().width = currentValue;
//刷新视图,即重新绘制,从而实现动画效果
mButton.requestLayout();
}
});
// 启动动画
animator.start();
3.3.3 ValueAnimator.ofObject 使用
// 创建初始动画时的对象 & 结束动画时的对象
myObject object1 = new myObject();
myObject object2 = new myObject();
ValueAnimator anim = ValueAnimator.ofObject(new myObjectEvaluator(), object1, object2);
// 创建动画对象 & 设置参数
// 参数说明
// 参数1:自定义的估值器对象(TypeEvaluator 类型参数) - 下面会详细介绍
// 参数2:初始动画的对象
// 参数3:结束动画的对象
anim.setDuration(5000);
anim.start();
ValueAnimator.ofObject(),从上面的工作原理可以看出并没有系统默认实现,因为对对象的动画操作复杂 & 多样,系统无法知道如何从初始对象过度到结束对象
因此,对于ValueAnimator.ofObject(),我们需自定义估值器(TypeEvaluator)来告知系统如何进行从 初始对象 过渡到 结束对象的逻辑
自定义实现的逻辑如下
//定义对象
public class Point {
// 设置两个变量用于记录坐标的位置
private float x;
private float y;
// 构造方法用于设置坐标
public Point(float x, float y) {
this.x = x;
this.y = y;
}
// get方法用于获取坐标
public float getX() {
return x;
}
public float getY() {
return y;
}
}
// 实现TypeEvaluator接口
public class PointEvaluator implements TypeEvaluator {
// 复写evaluate()
// 在evaluate()里写入对象动画过渡的逻辑
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 将动画初始值startValue 和 动画结束值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());
// 将计算后的坐标封装到一个新的Point对象中并返回
Point point = new Point(x, y);
return point;
}
}
//自定义View
/**
* Created by Carson_Ho on 17/4/18.
*/
public class MyView extends View {
// 设置需要用到的变量
public static final float RADIUS = 70f;// 圆的半径 = 70
private Point currentPoint;// 当前点坐标
private Paint mPaint;// 绘图画笔
// 构造方法(初始化画笔)
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// 初始化画笔
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLUE);
}
// 复写onDraw()从而实现绘制逻辑
// 绘制逻辑:先在初始点画圆,通过监听当前坐标值(currentPoint)的变化,每次变化都调用onDraw()重新绘制圆,从而实现圆的平移动画效果
@Override
protected void onDraw(Canvas canvas) {
// 如果当前点坐标为空(即第一次)
if (currentPoint == null) {
currentPoint = new Point(RADIUS, RADIUS);
// 创建一个点对象(坐标是(70,70))
// 在该点画一个圆:圆心 = (70,70),半径 = 70
float x = currentPoint.getX();
float y = currentPoint.getY();
canvas.drawCircle(x, y, RADIUS, mPaint);
// (重点关注)将属性动画作用到View中
// 步骤1:创建初始动画时的对象点 & 结束动画时的对象点
Point startPoint = new Point(RADIUS, RADIUS);// 初始点为圆心(70,70)
Point endPoint = new Point(700, 1000);// 结束点为(700,1000)
// 步骤2:创建动画对象 & 设置初始值 和 结束值
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
// 参数说明
// 参数1:TypeEvaluator 类型参数 - 使用自定义的PointEvaluator(实现了TypeEvaluator接口)
// 参数2:初始动画的对象点
// 参数3:结束动画的对象点
// 步骤3:设置动画参数
anim.setDuration(5000);
// 设置动画时长
// 步骤3:通过 值 的更新监听器,将改变的对象手动赋值给当前对象
// 此处是将 改变后的坐标值对象 赋给 当前的坐标值对象
// 设置 值的更新监听器
// 即每当坐标值(Point对象)更新一次,该方法就会被调用一次
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPoint = (Point) animation.getAnimatedValue();
// 将每次变化后的坐标值(估值器PointEvaluator中evaluate()返回的Piont对象值)到当前坐标值对象(currentPoint)
// 从而更新当前坐标值(currentPoint)
// 步骤4:每次赋值后就重新绘制,从而实现动画效果
invalidate();
// 调用invalidate()后,就会刷新View,即才能看到重新绘制的界面,即onDraw()会被重新调用一次
// 所以坐标值每改变一次,就会调用onDraw()一次
}
});
anim.start();
// 启动动画
} else {
// 如果坐标值不为0,则画圆
// 所以坐标值每改变一次,就会调用onDraw()一次,就会画一次圆,从而实现动画效果
// 在该点画一个圆:圆心 = (30,30),半径 = 30
float x = currentPoint.getX();
float y = currentPoint.getY();
canvas.drawCircle(x, y, RADIUS, mPaint);
}
}
}
3.4 ObjectAnimator
直接对对象的属性值进行改变操作,从而实现动画效果
3.4.1 java code
ObjectAnimator animator = ObjectAnimator.ofFloat(Object object, String property, float ....values);
// ofFloat()作用有两个
// 1. 创建动画实例
// 2. 参数设置:参数说明如下
// 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);
// 设置动画重复播放次数 = 重放次数+1
// 动画播放次数 = infinite时,动画无限重复
anim.setRepeatMode(ValueAnimator.RESTART);
// 设置重复播放动画模式
// ValueAnimator.RESTART(默认):正序重放
// ValueAnimator.REVERSE:倒序回放
animator.start();
// 启动动画
3.4.2 xml
步骤1:在路径 res/animator 的文件夹里创建动画效果.xml文件
此处设置为res/animator/set_animation.xml
步骤2:设置动画参数
set_animation.xml
// ObjectAnimator 采用 标签
在Java代码中启动动画
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.view_animation);
// 载入XML动画
animator.setTarget(view);
// 设置动画对象
animator.start();
// 启动动画
3.5 TypeEvaluator
估值器,决定初值如何变化到终值
示例参考3.3.3
3.6 Interpolator
animation.setInterpolator(new AccelerateInterpolator());
android:interpolator="@android:anim/accelerate_interpolator" 设置动画为加速动画(动画播放中越来越快)
animation.setInterpolator(new DecelerateInterpolator());
android:interpolator="@android:anim/decelerate_interpolator" 设置动画为减速动画(动画播放中越来越慢)
animation.setInterpolator(new AccelerateDecelerateInterpolator());
android:interpolator="@android:anim/accelerate_decelerate_interpolator" 设置动画为先加速在减速(开始速度最快 逐渐减慢)
animation.setInterpolator(new AnticipateInterpolator());
android:interpolator="@android:anim/anticipate_interpolator" 先反向执行一段,然后再加速反向回来(相当于我们弹簧,先反向压缩一小段,然后在加速弹出)
animation.setInterpolator(new AnticipateOvershootInterpolator());
android:interpolator="@android:anim/anticipate_overshoot_interpolator" 同上先反向一段,然后加速反向回来,执行完毕自带回弹效果(更形象的弹簧效果)
animation.setInterpolator(new BounceInterpolator());
android:interpolator="@android:anim/bounce_interpolator" 执行完毕之后会回弹跳跃几段(相当于我们高空掉下一颗皮球,到地面是会跳动几下)
animation.setInterpolator(new CycleInterpolator(2));
android:interpolator="@android:anim/cycle_interpolator" 循环,动画循环一定次数,值的改变为一正弦函数:Math.sin(2* mCycles* Math.PI* input)
animation.setInterpolator(new LinearInterpolator());
android:interpolator="@android:anim/linear_interpolator" 线性均匀改变
animation.setInterpolator(new OvershootInterpolator());
android:interpolator="@android:anim/overshoot_interpolator" 加速执行,结束之后回弹
3.7 组合动画
3.7.1 java code
// 步骤1:设置需要组合的动画效果
ObjectAnimator translation = ObjectAnimator.ofFloat(mButton, "translationX", curTranslationX, 300,curTranslationX);
// 平移动画
ObjectAnimator rotate = ObjectAnimator.ofFloat(mButton, "rotation", 0f, 360f);
// 旋转动画
ObjectAnimator alpha = ObjectAnimator.ofFloat(mButton, "alpha", 1f, 0f, 1f);
// 透明度动画
// 步骤2:创建组合动画的对象
AnimatorSet animSet = new AnimatorSet();
// 步骤3:根据需求组合动画
animSet.play(translation).with(rotate).before(alpha);
animSet.setDuration(5000);
// 步骤4:启动动画
animSet.start();
3.7.2 xml
步骤1:在 res/animator的文件夹里创建动画.xml文件
此处为 res/animator/set_animation.xml
步骤2:设置动画效果
set_animation.xml
// 表示Set集合内的动画按顺序进行
// ordering的属性值:sequentially & together
// sequentially:表示set中的动画,按照先后顺序逐步进行(a 完成之后进行 b )
// together:表示set中的动画,在同一时间同时进行,为默认值
// 下面的动画同时进行
// 下面的动画按序进行
在Java代码中启动动画
mButton = (Button) findViewById(R.id.Button);
// 创建动画作用对象:此处以Button为例
AnimatorSet animator = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.set_animation);
// 创建组合动画对象 & 加载XML动画
animator.setTarget(mButton);
// 设置动画作用对象
animator.start();
// 启动动画
3.8 ViewPropertyAnimator
View自由的一种属性动画的简写方式
// 使用解析
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()方法来启动动画
3.9 监听
3.9.1 AnimatorListener 纯接口
Animation.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) {
//动画结束时执行
}
});
3.9.3 监听适配器AnimatorListenerAdapter,不用全部重写
anim.addListener(new AnimatorListenerAdapter() {
// 向addListener()方法中传入适配器对象AnimatorListenerAdapter()
// 由于AnimatorListenerAdapter中已经实现好每个接口
// 所以这里不实现全部方法也不会报错
@Override
public void onAnimationStart(Animator animation) {
// 如想只想监听动画开始时刻,就只需要单独重写该方法就可以
}
});
4 特别注意
如何手动设置对象类属性的 set() & get()
a. 背景
ObjectAnimator 类 自动赋给对象的属性 的本质是调用该对象属性的set() & get()方法进行赋值
所以,ObjectAnimator.ofFloat(Object object, String property, float ....values)的第二个参数传入值的作用是:让ObjectAnimator类根据传入的属性名 去寻找 该对象对应属性名的 set() & get()方法,从而进行对象属性值的赋值
从上面的原理可知,如果想让对象的属性a的动画生效,属性a需要同时满足下面两个条件:
对象必须要提供属性a的set()方法
a. 如果没传递初始值,那么需要提供get()方法,因为系统要去拿属性a的初始值
b. 若该条件不满足,程序直接Crash
对象提供的 属性a的set()方法 对 属性a的改变 必须通过某种方法反映出来
a. 如带来ui上的变化
b. 若这条不满足,动画无效,但不会Crash)
上述条件,一般第二条都会满足,主要是在第一条
比如说:由于View的setWidth()并不是设置View的宽度,而是设置View的最大宽度和最小宽度的;所以通过setWidth()无法改变控件的宽度;所以对View视图的width做属性动画没有效果
具体请看下面Button按钮的例子
Button mButton = (Button) findViewById(R.id.Button);
// 创建动画作用对象:此处以Button为例
// 此Button的宽高设置具体为具体宽度200px
ObjectAnimator.ofInt(mButton, "width", 500).setDuration(5000).start();
// 设置动画的对象,但这个动画不会有任何效果
5 核心源码及注释
http://blog.csdn.net/u013378580/article/details/51917884