- 逐帧动画:在时间轴的每帧上逐帧绘制不同的内容,使其连续播放而成动画。
- 补间动画:对View进行一系列的动画操作,包括淡入淡出、缩放、平移、旋转四种。
- 属性动画(property animation):是一种不断地对值进行操作的机制,并将值赋值到指定对象的指定属性上,可以是任意对象的任意属性。
为什么要引入属性动画(Android 3.0版本)?
补间动画只能作用在View上;且只能实现淡入淡出、缩放、平移、旋转这四种动画操作;它只是改变了View的显示效果而已,却没有改变View的属性,也就是说移动button后对Button的点击事件会失效,因为属性坐标没有相应的更新。
属性动画主要有四个点:ValueAnimator、ObjectAnimator、Interpolator、TypeEvaluator
ValueAnimator
它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等。需要手动对View进行操作。
ValueAnimator类中有3个重要方法:
- ValueAnimator.ofInt(int values) //采用默认的整型估值器(IntEvaluator)
- ValueAnimator.ofFloat(float values) //采用默认的浮点型估值器 (FloatEvaluator)
- ValueAnimator.ofObject(int values)//将初始值以对象的形式过渡到结束值,需要自定义估值器,将多个值 封装到一个对象里进行统筹操作。
// 设置动画延迟播放时间
valueAnimator.setStartDelay(500);
// 设置动画重复播放次数 = 重放次数+1
// 动画播放次数 = infinite时,动画无限重复
valueAnimator.setRepeatCount(0);
// 设置重复播放动画模式
// ValueAnimator.RESTART(默认):正序重放
// ValueAnimator.REVERSE:倒序回放
valueAnimator.setRepeatMode(ValueAnimator.RESTART);
复制代码
示例: ValueAnimator.addUpdateListener监听这个动画的改变,监听到valueAnimator的变化,则手动对对象进行操作
//Point只是一个简单的点坐标
data class Point(var x: Float, var y: Float)
/**
* 移动 ofObject
* PointEvaluator实现了TypeEvaluator,具体实现见下文TypeEvaluator
*/
private void changeIcon1() {
Point startPoint = new Point(icon.getX(), icon.getY());// 初始点为圆心
Point endPoint = new Point(0, 0); // 结束点为(0,0)
ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
valueAnimator.setDuration(2000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Point currentPoint = (Point) animation.getAnimatedValue();
Log.e("zhen", "currentValue: " + currentPoint.toString());
icon.setX(currentPoint.getX());
icon.setY(currentPoint.getY());
icon.requestLayout();
}
});
valueAnimator.start();
}
/**
* 缩小 ofInt
*/
private void changeIcon() {
ValueAnimator valueAnimator = ValueAnimator.ofInt(icon.getLayoutParams().width,
DensityUtil.dp2px(this, 36));
valueAnimator.setDuration(2000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int currentValue = (int) animation.getAnimatedValue();
Log.e("zhen", "currentValue: " + currentValue);
icon.getLayoutParams().width = currentValue;
icon.getLayoutParams().height = currentValue;
icon.requestLayout();
}
});
valueAnimator.start();
}
复制代码
ObjectAnimator:
ObjectAnimator继承自ValueAnimator,即具备ValueAnimator的所有属性
通过不断控制值的变化,再不断自动赋给对象的属性,从而实现动画效果。
ObjectAnimator.ofFloat(Object object, String property, float ....values)指定了要操作的对象object;根据property属性名去寻找该对象对应属性名的 set() & get()方法,从而进行对象属性值的赋值;values指定了赋值的初始值和结束值
如果需要采用ObjectAnimator 类实现动画效果,那么需要操作的对象就必须有该属性的set() & get();
除了rotation、rotationX、rotationY、alpha、scale、scaleX、scaleY、translationX、translationY,还可以自定义属性。
//垂直方向的大小缩小为0.5倍
private void changeNameWithScaleY() {
ObjectAnimator animator = ObjectAnimator.ofFloat(tvName,"scaleY", 1, 0.5F);
animator.setDuration(2000);
animator.start();
}
//水平方向的大小缩小为0.5倍
private void changeNameWithScaleX() {
ObjectAnimator animator = ObjectAnimator.ofFloat(tvName,"scaleX", 1, 0.5F);
animator.setDuration(2000);
animator.start();
}
//Y方向移动,当前控件所在位置为原点,遵循android坐标系
private void changeNameWithTransitionY() {
ObjectAnimator animator = ObjectAnimator.ofFloat(tvName,"translationY", tvName.getY(), 0);
animator.setDuration(2000);
animator.start();
}
//X方向移动,当前控件所在位置为原点,遵循android坐标系
private void changeNameWithTransitionX() {
ObjectAnimator animator = ObjectAnimator.ofFloat(tvName,"translationX", 100, 300);
animator.setDuration(2000);
animator.start();
}
//旋转
private void changeNameWithRotate() {
ObjectAnimator animator = ObjectAnimator.ofFloat(tvName,"rotation", 0, 180, 0);
animator.setDuration(2000);
animator.start();
}
//改变透明度,从不透明1变为透明0,又变为不透明1
private void changeNameWithAlpha() {
ObjectAnimator animator = ObjectAnimator.ofFloat(tvName,"alpha", 1f, 0.5f, 1);
animator.setDuration(2000);
animator.start();
}
复制代码
插值器(Interpolator):
决定值的变化模式(匀速、加速、弹跳效果)
除了系统自带的interpolator,还可自定义interpolator
- AccelerateDecelerateInterpolator: 在动画开始与结束的地方速率改变比较慢,在中间的时候加速
- AccelerateInterpolator: 在动画开始的地方速率改变比较慢,然后开始加速
- AnticipateInterpolator: 开始的时候向后然后向前甩
- AnticipateOvershootInterpolator: 开始的时候向后然后向前甩一定值后返回最后的值
- BounceInterpolator: 动画结束的时候弹起
- CycleInterpolator: 动画循环播放特定的次数,速率改变沿着正弦曲线
- DecelerateInterpolator: 在动画开始的地方快然后慢
- LinearInterpolator: 以常量速率改变
- OvershootInterpolator: 向前甩一定值后再回到原来位置
//在animator之前设置interpolator 为加速运动模型
valueAnimator.setInterpolator(new AccelerateInterpolator());
复制代码
估值器(TypeEvaluator)
设置动画如何从初始值过渡到结束值的逻辑
相当于是你告诉了系统你的起点、终点和消耗时间,系统自动的为你规划好了一条路线。
系统内置的Evaluator: IntEvaluator、FloatEvaluator,都是实现的TypeEvaluator。
public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
复制代码
public class FloatEvaluator implements TypeEvaluator<Number> {
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}
复制代码
自定义TypeEvaluator
fraction表示动画的完成度,第二三个参数分别表示动画的初始值和结束值
这里实现的是上面的小圆点Point (将x坐标、y坐标封装为一个整体,交给我们去处理)
//PointEvaluator实现了TypeEvaluator
public class PointEvaluator implements TypeEvaluator {
@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;
}
}
复制代码
AnimationSet组合动画
AnimatorSet.play(Animator anim) :播放当前动画
AnimatorSet.after(long delay) :将现有动画延迟x毫秒后执行
AnimatorSet.with(Animator anim) :将现有动画和传入的动画同时执行
AnimatorSet.after(Animator anim) :将现有动画插入到传入的动画之后执行
AnimatorSet.before(Animator anim) : 将现有动画插入到传入的动画之前执行
复制代码
让name随着icon一起移动到左上角,changeName用的是animatorSet
translation默认所操作的控件为坐标原点,其余坐标操作遵循android坐标系
private void changeName() {
ObjectAnimator animatorX = ObjectAnimator.ofFloat(tvName,"translationX", 0, - tvName.getX() + DensityUtil.dp2px(this, 58));
ObjectAnimator animatorY = ObjectAnimator.ofFloat(tvName,"translationY", 0, - tvName.getY());
ObjectAnimator animatorScale = ObjectAnimator.ofFloat(tvName,"scale", 1, 0.5F);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(animatorX).with(animatorY).with(animatorScale);
animatorSet.setDuration(2000);
animatorSet.start();
}
复制代码
animator的监听
AnimatorListenerAdapter实现了Animator.AnimatorListener和Animator.AnimatorPauseListener,可实现其中1-n个方法
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
//在动画开始的时候调用
}
@Override
public void onAnimationEnd(Animator animation) {
//在动画结束的时候调用
}
@Override
public void onAnimationCancel(Animator animation) {
//在动画被取消的时候调用
}
@Override
public void onAnimationRepeat(Animator animation) {
//在动画重复执行的时候调用
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
}
});
//AnimatorListenerAdapter实现了Animator.AnimatorListener和Animator.AnimatorPauseListener,可实现其中1-n个方法
public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener,
Animator.AnimatorPauseListener
复制代码