Android动画探索之属性动画

这篇文章来总结下属性动画,通过下面几点来了解下属性动画的内容:

  • 属性动画概述
  • 属性动画工作原理
  • ValueAnimator
  • ObjectAnimator
  • ValueAnimator和ObjectAnimator区别
  • 插值器
  • 估值期
  • 插值器和估值期的区别
一.概述
1.特性
属性动画是API 11新加入的特性,相比于View动画只作用在视图View上,它对作用对象进行了扩展,属性动画可以对任何对象做动画,甚至可以没有对象.除了作用对象进行了扩展以外,属性动画的效果也得到了加强,不再像View动画那样只能支持四种简单的变换.另外View动画没有改变View的属性,只是改变了视觉效果.而属性动画是通过改变对象的属性来实现动画效果.
2.核心类
Android动画探索之属性动画
文章图片

Android动画探索之属性动画
文章图片

二.工作原理
在一定时间间隔内,通过不断对值进行改变,并不断将该值赋值给对象的属性,从而实现该对象在该属性上的动画效果.工作原理图如下:
Android动画探索之属性动画
文章图片

如上图所示,属性动画用到了几个核心的类,如下图所示,下面具体介绍这几个类的使用.
Android动画探索之属性动画
文章图片

三.ValueAnimator
通过不断控制值的变化,再不断手动赋值给对象的属性,从而实现动画效果.
对于控制的值的不同,Android提供了三种构造方法来实例化ValueAnimator对象.
ValueAnimator.ofInt(int… values);
ValueAnimator.ofFloat(float… values);
ValueAnimator.ofObject(TypeEvaluator evaluator, Object… values);
其工作原理图如下:
Android动画探索之属性动画
文章图片

1.ValueAnimator.ofInt(int… values)
(1)概念
将初始值以整型数值的形式过渡到结束值,即估值器是整型估值器(IntEvaluator)
(2)具体使用
我们这里实现把一个按钮的宽度从150px放大到500的案例,可以通过xml和代码的方式实现。
a.代码的方式
final Button bt = findViewById(R.id.bt); //步骤1:传入动画属性的初始值&结束值 //创建实例,传入的多个int参数,表示初始到结束过渡的值,其中内置整型估值器 ValueAnimator valueAnimator = ValueAnimator.ofInt(bt.getLayoutParams().width, 500); //步骤2:设置动画播放的各种属性 //设置动画运行的时长 valueAnimator.setDuration(2000); //设置动画延迟播放时间 valueAnimator.setStartDelay(500); //设置动画重复播放次数 valueAnimator.setRepeatCount(0); //设置动画重复播放模式,RESTART默认正序播放,REVERSE倒序播放 valueAnimator.setRepeatMode(ValueAnimator.RESTART); //步骤3:通过动画的更新监听,将改变的值手动赋值给对象的属性值 valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int currentValue = https://www.it610.com/article/(int) animation.getAnimatedValue(); bt.getLayoutParams().width = currentValue; //步骤4:刷新视图,即重新绘制,从而实现动画效果 bt.requestLayout(); } }); //步骤5: 开启动画 valueAnimator.start();

b.xml的方式
// res/animator/animator_bt.xml

//载入xml动画 Animator animator = AnimatorInflater.loadAnimator(this, R.animator.animator_bt); //设置动画对象 animator.setTarget(view); //开启动画 animator.start();

注意:实际开发中,建议使用代码的方式实现属性动画,因为很多属性的初始值无法提前确定,只能代码动态获取。
2.ValueAnimator.ofFloat(float… values)
(1)概念:
将初始值以浮点型数值的形式过渡到结束值,即估值器是浮点估值器(FloatEvaluator)
(2)具体实现
其可以通过xml和代码的方式来实现,使用细节和ValueAnimator.ofInt()类型,这里就不多说了。
3.ValueAnimator.ofObject(TypeEvaluator evaluator, Object… values)
(1)概念
初始值以对象的的形式过渡到结束值。
(2)具体使用
//步骤一:创建初始动画时的对象&创建结束动画时对象 MyObject myObject1 = new MyObject(); MyObject myObject2 = new MyObject(); //步骤二:创建动画实例,参数分别是自定义估值器对象,初始动画的对象,结束动画的对象 ValueAnimator valueAnimator = ValueAnimator.ofObject(new MyObjectEvaluator(), myObject1, myObject2); //步骤三:设置动画参数 valueAnimator.setStartDelay(500); //步骤四:开始动画 valueAnimator.start();

(3)估值器(TypeEvaluator)
作用:设置动画如何从初始值过渡到结束值的逻辑。
a.系统内置的估值器
通过上面学习ValueAnimator.ofInt()和ValueAnimator.ofFloat()知道系统内置了一个IntEvaluator估值器和FloatEvaluator估值器,实现了将初始值以整型或浮点型的形式过渡到结束值的逻辑,下面以FloatEvaluator为例看下源代码:
public class FloatEvaluator implements TypeEvaluator { public Float evaluate(float fraction, Number startValue, Number endValue) { //fraction 表示动画完成度 //startValue,endValue,表示初始值和结束值 float startFloat = startValue.floatValue(); //返回当前动画的值 return startFloat + fraction * (endValue.floatValue() - startFloat); }

b.自定义估值器
ValueAnimator.ofInt()和ValueAnimator.ofFloat()内置了系统估值器,即系统已经默认实现了如何从初始值过渡到结束值的逻辑,但是ValueAnimator.ofObject()没有系统默认实现,因为对象的动画复杂多样,系统无法知道如何从初始对象过渡到结束对象。所以要自定义估值器告诉系统如何从初始对象过渡到结束对象,自定义估值器的逻辑如下:
//实现TypeEvaluator接口 public class ObjectEvaluator implements TypeEvaluator { //复写evaluate方法 @Override public Object evaluate(float fraction, Object startValue, Object endValue) { //参数:动画完成度、开始值、结束值 //写对象动画过渡逻辑 //返回对象动画过渡逻辑计算后的值 return null; } }

c.案例说明
这里实现一个圆从一个点移动到另外一个点。
步骤1:定义对象类
public class Point { //定义坐标位置 private float x; private float y; //设置坐标 public Point(float x, float y) { this.x= x; this.y = y; } //获取坐标 public float getX() { return x; }public float getY() { return y; }}

步骤2:自定义估值器
public class PointEvaluator implements TypeEvaluator { @Override public Point evaluate(float fraction, Object startValue, Object endValue) { //将初始值和结束值强制转换为Point对象 Point startPoint = (Point)startValue; Point endPoint = (Point)endValue; //计算当前动画x,y的值 float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX()); float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY()); //将计算后的值封装成Point对象返回 return new Point(x,y); } }

步骤3:将属性动画应用到自定义View当中
public class PointView extends View { public final static float RADIUS = 70f; //圆的半径 privatePaint paint; private Point currentPoint; //当前坐标public PointView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); //初始化画笔 paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.BLUE); }@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (currentPoint == null) { //创建点对象 currentPoint = new Point(RADIUS,RADIUS); float x = currentPoint.getX(); float y = currentPoint.getY(); //画一个坐标为x,y,半径为RADIUS的圆 canvas.drawCircle(x,y,RADIUS,paint); //将属性动画作用在View上 //步骤1:初始化初始对象值和结束对象值 Point startPoint = new Point(RADIUS, RADIUS); Point endPoint = new Point(700,1000); //步骤2:创建动画对象 ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint); //步骤3:设置动画参数 valueAnimator.setDuration(5000); //步骤4:通过值的更新监听器,将变化后的对象手动赋值给当前对象 valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { currentPoint = (Point)animation.getAnimatedValue(); //更新当前坐标值,每次赋值后重新绘制,从而达到动画效果 invalidate(); } }); valueAnimator.start(); } else { float x = currentPoint.getX(); float y = currentPoint.getY(); canvas.drawCircle(x,y,RADIUS,paint); }} }

步骤4:在布局中使用

四.ObjectAnimator
1.概念
通过不断控制值的变化,再不断自动赋值给对象的属性,从而实现动画效果。它继承自ValueAnimator类,其原理图如下:
Android动画探索之属性动画
文章图片

2.具体使用
(1)代码的方式:
ObjectAnimator的四种动画效果:
a.alpha
b.scaleX/scaleY
c.translationX/translationY
d.rotation
ImageView imageView = findViewById(R.id.image); //透明度动画 ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "alpha", 1, 0, 1); animator.setDuration(5000); animator.start(); //缩放动画 AnimatorSet animatorSet = new AnimatorSet(); imageView.setPivotX(imageView.getWidth()+250); imageView.setPivotY(imageView.getHeight()+250); animatorSet.playTogether( ObjectAnimator.ofFloat(imageView,"scaleX",1,2) .setDuration(5000), ObjectAnimator.ofFloat(imageView,"scaleY",1,2) .setDuration(5000) ); animatorSet.start(); //平移动画 AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether( ObjectAnimator.ofFloat(imageView,"translationX",20,100) .setDuration(5000), ObjectAnimator.ofFloat(imageView,"translationY",20,100) .setDuration(5000) ); animatorSet.start(); //旋转动画 ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "rotation", 0, 360); animator.setDuration(5000); animator.start();

(2)xml的方式
// res/animator/animator_obj.xml

ImageView imageView = findViewById(R.id.image); Animator animator = AnimatorInflater.loadAnimator(this, R.animator.animator_obj); animator.setTarget(imageView); animator.start();

3.ValueAnimator和ObjectAnimator对比
(1)相同点:都是属性动画,都是通过先改变值,再赋值给对象的属性从而实现动画效果。
(2)不同点:ValueAnimator类是先改变值,再手动的赋值给对象的属性,从而实现动画效果,是间接对对象的属性进行操作;ObjectAnimator类是先改变值,再自动的赋值给对象的属性,从而实现动画效果,是直接对对象属性进行操作。
五.属性动画监听
属性动画主要通过两个接口来监听动画的播放过程:AnimatorUpdateListener和AnimatorListener,
animator.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) {} });

//AnimatorListenerAdapter类是AnimatorListener的适配器, 可以选择性的复写上面的四个方法,在开发中能够更加灵活方便的使用 animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); } });

六.插值器(Interpolator)
1.概念
Android实现动画效果的一个辅助接口,表示属性值从初始值过渡到结束值的变化规律,比如匀速、加速、减速等。
2.具体的使用
(1)代码中使用
ImageView imageView = findViewById(R.id.image); Animator animator = AnimatorInflater.loadAnimator(this, R.animator.animator_obj); animator.setTarget(imageView); animator.setInterpolator(new LinearInterpolator()); animator.start();

(2)xml中使用

3.系统内置插值器
Android动画探索之属性动画
文章图片

注意:系统默认的插值器是AccelerateDecelerateInterpolator,先加速再减速。
4.自定义插值器
自定义插值器主要是根据动画的进度(0%-100%)计算出当前属性值改变的百分比,需要实现Interpolator/TimeInterpolator接口,复写getInterpolation方法。其中View动画实现Interpolator接口,属性动画实现TimeInterpolator接口,TimeInterpolator接口是属性动画新增的,用于兼容Interpolator接口。
(1)我们先来看一下系统内置的匀速插值器和加速减速插值器。
@HasNativeInterpolator public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { ... public float getInterpolation(float input) { //匀速插值器没有对input做处理,而是直接返回,这是因为input也是匀速增加,所以fraction值也会匀速增加,实现迅速的动画效果 return input; } ... }@HasNativeInterpolator public class AccelerateDecelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { ... public float getInterpolation(float input) { //加速减速差值器,使用了余弦函数 //因为input取值是0到1,那么(input + 1) * Math.PI的值是π到2π //因此cos(π)结果时-1,cos(2π)的结果时1 //(Math.cos((input + 1) * Math.PI) / 2.0f) 的取值范围也就是-0.5到0.5 //因此最后的返回值还是0到1,但是通过余弦计算后,不再是匀速的,而是先加速后减速 return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; } ... }

(2)自定义一个插值器,实现减速加速
public class DecelerateAccelerateInterpolator implements TimeInterpolator { @Override public float getInterpolation(float input) { float result; //这里实现一个减速加速的插值器,使用了正弦函数 if (input <= 0.5) { result = (float) Math.sin(Math.PI*input)/2; } else { result = 2-(float)(Math.sin(Math.PI*input)/2); } return result; } }

5.插值器和估值器的区别
【Android动画探索之属性动画】Android动画探索之属性动画
文章图片

学习文章:
Android:这份属性动画的核心使用类ValueAnimator学习指南请收好!

    推荐阅读