android|Android Animator(Android动画)

ObjectAnimator 基本使用 一、概述 1、引入
上几篇给大家讲了 ValueAnimator,但 ValueAnimator 有个缺点,就是只能对数值对动画计算。我们要想对哪个控件操作,需要监听动画过程,在监听中对控件操作。这样使用起来相比补间动画而言就相对比较麻烦。 为了能让动画直接与对应控件相关联,以使我们从监听动画过程中解放出来,谷歌的开发人员在 ValueAnimator 的基础上,又派生了一个类 ObjectAnimator; 由于 ObjectAnimator 是派生自 ValueAnimator 的,所以 ValueAnimator 中所能使用的方法,在 ObjectAnimator 中都可以正常使用。 但 ObjectAnimator 也重写了几个方法,比如 ofInt(),ofFloat()等。我们先看看利用 ObjectAnimator 重写的 ofFloat 方法如何实现一个动画:(改变透明度)

ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"alpha",1,0,1); animator.setDuration(2000); animator.start();

【android|Android Animator(Android动画)】效果图如下:

我们这里还是直接使用上一篇的框架代码; (当点击 start anim 时执行动画)从上面的代码中可以看到构造 ObjectAnimator 的方法非常简单:
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)

  • 第一个参数用于指定这个动画要操作的是哪个控件
  • 第二个参数用于指定这个动画要操作这个控件的哪个属性
  • 第三个参数是可变长参数,这个就跟 ValueAnimator 中的可变长参数的意义一样了,就是指这个属性值是从哪变到哪。像我们上面的代码中指定的就是将 textview 的 alpha 属性从 0 变到 1 再变到 0; 下面我们再来看一下如何实现旋转效果:
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotation",0,180,0); animator.setDuration(2000); animator.start();

效果图如下:

从代码中可以看到,我们只需要改变 ofFloat()的第二个参数的值就可以实现对应的动画; 那么问题来了,我们怎么知道第二个参数的值是啥呢?
2、setter 函数
我们再回来看构造改变 rotation 值的 ObjectAnimator 的方法
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotation",0,180,0);

TextView 控件有 rotation 这个属性吗?没有,不光 TextView 没有,连它的父类 View 中也没有这个属性。那它是怎么来改变这个值的呢?其实,ObjectAnimator 做动画,并不是根据控件 xml 中的属性来改变的,而是通过指定属性所对应的 set 方法来改变的。比如,我们上面指定的改变 rotation 的属性值,ObjectAnimator 在做动画时就会到指定控件(TextView)中去找对应的 setRotation()方法来改变控件中对应的值。同样的道理,当我们在最开始的示例代码中,指定改变”alpha”属性值的时候,ObjectAnimator 也会到 TextView 中去找对应的 setAlpha()方法。那 TextView 中都有这些方法吗,有的,这些方法都是从 View 中继承过来的,在 View 中有关动画,总共有下面几组 set 方法:
//1、透明度:alpha public void setAlpha(float alpha)//2、旋转度数:rotation、rotationX、rotationY public void setRotation(float rotation) public void setRotationX(float rotationX) public void setRotationY(float rotationY)//3、平移:translationX、translationY public void setTranslationX(float translationX) public void setTranslationY(float translationY)//缩放:scaleX、scaleY public void setScaleX(float scaleX) public void setScaleY(float scaleY)

可以看到在 View 中已经实现了有关 alpha,rotaion,translate,scale 相关的 set 方法。所以我们在构造 ObjectAnimator 时可以直接使用。 在开始逐个看这些函数的使用方法前,我们先做一个总结: 1、要使用 ObjectAnimator 来构造对画,要操作的控件中,必须存在对应的属性的 set 方法 2、setter 方法的命名必须以骆驼拼写法命名,即 set 后每个单词首字母大写,其余字母小写,即类似于 setPropertyName 所对应的属性为 propertyName 下面我们就来看一下上面中各个方法的使用方法及作用。 有关 alpha 的用法,上面已经讲过了,下面我们来看看其它的
(1)、setRotationX、setRotationY 与 setRotation
  • setRotationX(float rotationX):表示围绕 X 轴旋转,rotationX 表示旋转度数
  • setRotationY(rotationY):表示围绕 Y 轴旋转,rotationY 表示旋转度数
  • setRotation(float rotation):表示围绕 Z 旋转,rotation 表示旋转度数
先来看看 setRotationX 的效果:
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotationX",0,270,0); animator.setDuration(2000); animator.start();

效果图如下:

从效果图中明显看出,textview 的旋转方法是围绕 X 轴旋转的,我们设定为从 0 度旋转到 270 度再返回 0 度。 然后再来看看 setRotationY 的使用方法与效果:
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotationY",0,180,0); animator.setDuration(2000); animator.start();

效果图如下:

从效果图中明显可以看出围绕 Y 轴旋转的。 我们再来看看 setRotation 的用法与效果:
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotation",0,270,0); animator.setDuration(2000); animator.start();


我们上面说了,setRotation 是围绕 Z 轴旋转的,可能有些同学不理解什么是 Z 轴,我们来看一张图:

从这张图中,绿色框部分表示手机屏幕,很明显可以看出 Z 轴就是从屏幕左上角原点向外伸出的一条轴。这样,我们也就可以理解围绕 Z 轴旋转,为什么是这样子转了。
(2)、setTranslationX 与 setTranslationY
  • setTranslationX(float translationX) :表示在 X 轴上的平移距离,以当前控件为原点,向右为正方向,参数 translationX 表示移动的距离。
  • setTranslationY(float translationY) :表示在 Y 轴上的平移距离,以当前控件为原点,向下为正方向,参数 translationY 表示移动的距离。 我们先看看 setTranslationX 的用法:
ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "translationX", 0, 200, -200,0); animator.setDuration(2000); animator.start();

效果图如下:

所以,我们上面在构造动画时,指定的移动距离是(0, 200, -200,0),所以控件会从自身所有位置向右移动 200 像素,然后再移动到距离原点-200 的位置,最后回到原点; 然后我们来看看 setTranslateY 的用法:
ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "translationY", 0, 200, -100,0); animator.setDuration(2000); animator.start();

效果图如下:(为了方便看到效果,将 textview 垂直居中)

同样,移动位置的坐标也都是以当前控件所在位置为中心点的。所以对应的移动位置从原点移动向下移动 200 像素,然后再移动到向下到距原点 200 像素的位置,最后再回到(0,0)从效果图中很明显可以看出来。 从上面可以看出:每次移动距离的计算都是以原点为中心的;比如初始动画为 ObjectAnimator.ofFloat(tv, “translationY”, 0, 200, -100,0)表示首先从 0 移动到正方向 200 的位置,然后再移动到负方向 100 的位置,最后移动到原点。
(3)、setScaleX 与 setScaleY
  • setScaleX(float scaleX):在 X 轴上缩放,scaleX 表示缩放倍数
  • setScaleY(float scaleY):在 Y 轴上缩放,scaleY 表示缩放倍数 我们来看看 setScaleX 的用法:
ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "scaleX", 0, 3, 1); animator.setDuration(2000); animator.start();

效果图如下:

在效果图中,从 0 倍放大到 3 倍,然后再还原到 1 倍的原始状态。 然后再来看看 setScaleY 的用法
ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "scaleY", 0, 3, 1); animator.setDuration(2000); animator.start();

为了更好的看到效果,我把 textview 垂直居中了,效果图如下:

源码在文章底部给出 好了,到这里有关 View 中自带的 set 函数讲完了,我们来看看 ObjectAnimator 是如何实现控件动画效果的。
3、ObjectAnimator 动画原理 我们先来看张图:

在这张图中,将 ValueAnimator 的动画流程与 ObjectAnimator 的动画流程做了个对比。 可以看到 ObjectAnimator 的动画流程中,也是首先通过加速器产生当前进度的百分比,然后再经过 Evaluator 生成对应百分比所对应的数字值。这两步与 ValueAnimator 是完全一样的,唯一不同的是最后一步,在 ValueAnimator 中,我们要通过添加监听器来监听当前数字值。而在 ObjectAnimator 中,则是先根据属性值拼装成对应的 set 函数的名字,比如这里的 scaleY 的拼装方法就是将属性的第一个字母强制大写后,与 set 拼接,所以就是 setScaleY。然后通过反射找到对应控件的 setScaleY(float scaleY)函数,将当前数字值做为 setScaleY(float scale)的参数将其传入。 这里在找到控件的 set 函数以后,是通过反射来调用这个函数的,有关反射的使用大家可以参考《夯实 JAVA 基本之二 —— 反射(1):基本类周边信息获取》 这就是 ObjectAnimator 的流程,最后一步总结起来就是调用对应属性的 set 方法,将动画当前数字值做为参数传进去。
根据上面的流程,这里有几个注意事项: (1)、拼接 set 函数的方法:上面我们也说了是首先是强制将属性的第一个字母大写,然后与 set 拼接,就是对应的 set 函数的名字。注意,只是强制将属性的第一个字母大写,后面的部分是保持不变的。反过来,如果我们的函数名命名为 setScalePointX(float ),那我们在写属性时可以写成”scalePointX”或者写成“ScalePointX”都是可以的,即第一个字母大小写可以随意,但后面的部分必须与 set 方法后的大小写保持一致。(2)、如何确定函数的参数类型:上面我们知道了如何找到对应的函数名,那对应的参数方法的参数类型如何确定呢?我们在讲 ValueAnimator 的时候说过,动画过程中产生的数字值与构造时传入的值类型是一样的。由于 ObjectAnimator 与 ValueAnimator 在插值器和 Evaluator 这两步是完全一样的,而当前动画数值的产生是在 Evaluator 这一步产生的,所以 ObjectAnimator 的动画中产生的数值类型也是与构造时的类型一样的。那么问题来了,像我们的构造方法。
ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "scaleY", 0, 3, 1);

由于构造时使用的是 ofFloat 函数,所以中间值的类型应该是 Float 类型的,所以在最后一步拼装出来的 set 函数应该是 setScaleY(float xxx)的样式;这时,系统就会利用反射来找到 setScaleY(float xxx)函数,并把当前的动画数值做为参数传进去。 那问题来了,如果没有类似 setScaleY(float xxx)的函数,我们只实现了一个 setScaleY(int xxx)的函数怎么办?这里虽然函数名一样,但参数类型是不一样的,那么系统就会报一个错误:

意思就是对应函数的指定参数类型没有找到。 (3)、调用 set 函数以后怎么办?从 ObjectAnimator 的流程可以看到,ObjectAnimator 只负责把动画过程中的数值传到对应属性的 set 函数中就结束了,注意传给 set 函数以后就结束了!set 函数就相当我们在 ValueAnimator 中添加的监听的作用,set 函数中的对控件的操作还是需要我们自己来写的。
那我们来看看 View 中的 setScaleY 是怎么实现的吧:
/** * Sets the amount that the view is scaled in Y around the pivot point, as a proportion of * the view's unscaled width. A value of 1 means that no scaling is applied. * * @param scaleY The scaling factor. * @see #getPivotX() * @see #getPivotY() * * @attr ref android.R.styleable#View_scaleY */ public void setScaleY(float scaleY) { ensureTransformationInfo(); final TransformationInfo info = mTransformationInfo; if (info.mScaleY != scaleY) { invalidateParentCaches(); // Double-invalidation is necessary to capture view's old and new areas invalidate(false); info.mScaleY = scaleY; info.mMatrixDirty = true; mPrivateFlags |= DRAWN; // force another invalidation with the new orientation invalidate(false); } }

大家不必理解这一坨代码的意义,因为这些代码是需要读懂 View 的整体流程以后才能看得懂的,只需要跟着我的步骤来理解就行。这段代码总共分为两部分:第一步重新设置当前控件的参数,第二步调用 Invalidate()强制重绘; 所以在重绘时,控件就会根据最新的控件参数来绘制了,所以我们就看到当前控件被缩放了。 (4)、set 函数调用频率是多少:由于我们知道动画在进行时,每隔十几毫秒会刷新一次,所以我们的 set 函数也会每隔十几毫秒会被调用一次。 讲了这么多,就是为了强调一点:ObjectAnimator 只负责把当前运动动画的数值传给 set 函数。至于 set 函数里面怎么来做,是我们自己的事了。 好了,在知道了 ObjectAnimator 的原理以后,下面就来看看如何自定义一个 ObjectAnimator 的属性吧。
二、自定义 ObjectAnimator 属性 上面我们已经看了使用 View 自带的 set 函数所对应属性的方法,而且理解了 ObjectAnimator 的动画实现原理,下面我们来自定义一个属性来看看实现效果吧。 我们在开始之前再来捋一下 ObjectAnimator 的动画设置流程:ObjectAnimator 需要指定操作的控件对象,在开始动画时,到控件类中去寻找设置属性所对应的 set 函数,然后把动画中间值做为参数传给这个 set 函数并执行它。 所以,我们说了,控件类中必须所要设置属性所要对应的 set 函数。所以为了自由控制控件的实现,我们这里自定义一个控件。大家知道在这个自定义控件中,肯定存在一个 set 函数与我们自定义的属性相对应。 我们先来看看这段要实现的效果:

这个效果图与我们上篇自定义控件实现的效果差不多,这个控件中存在一个圆形,也是在动画时先将这个圆形放大,然后再将圆形还原。
1、保存圆形信息类——Point
为了,保存圆形的信息,我们先定义一个类:(Point.java)
public class Point { private int mRadius; public Point(int radius){ mRadius = radius; }public int getRadius() { return mRadius; }public void setRadius(int radius) { mRadius = radius; } }

这个类很好理解,只有一个成员变量 mRadius,表示圆的半径。
2、自定义控件——MyPointView
然后我们自定义一个控件 MyPointView,完整代码如下:
public class MyPointView extends View { private Point mPoint = new Point(100); public MyPointView(Context context, AttributeSet attrs) { super(context, attrs); }@Override protected void onDraw(Canvas canvas) { if (mPoint != null){ Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.RED); paint.setStyle(Paint.Style.FILL); canvas.drawCircle(300,300,mPoint.getRadius(),paint); } super.onDraw(canvas); }void setPointRadius(int radius){ mPoint.setRadius(radius); invalidate(); }}

在这段代码中,首先来看我们前面讲到的 set 函数:
void setPointRadius(int radius){ mPoint.setRadius(radius); invalidate(); }

第一点,这个 set 函数所对应的属性应该是 pointRadius 或者 PointRadius。前面我们已经讲了第一个字母大小写无所谓,后面的字母必须保持与 set 函数完全一致。 第二点,在 setPointRadius 中,先将当前动画传过来的值保存到 mPoint 中,做为当前圆形的半径。然后强制界面刷新 在界面刷新后,就开始执行 onDraw()函数:
@Override protected void onDraw(Canvas canvas) { if (mPoint != null){ Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.RED); paint.setStyle(Paint.Style.FILL); canvas.drawCircle(300,300,mPoint.getRadius(),paint); } super.onDraw(canvas); }



转载出处:
csdn:https://github.com/harvic/BlogResForGitHub
?? http://wiki.jikexueyuan.com/project/android-animation/7.html ??

    推荐阅读