Android动画

来源:互联网 发布:union摄影软件安卓版 编辑:程序博客网 时间:2024/05/22 08:10

Android动画

Androidde的动画可以分为三种:逐帧动画、视图动画、属性动画。

  • 帧动画:创建一个Drawable序列,这些Drawable可以按照指定的时间间隔一个一个的显示,也就是顺序播放事先做好的图像。
  • 视图动画:一种补间动画,是一种渐近式动画,通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度{淡出/淡入})从而产生动画效果。视图动画支持自定义。
  • 属性动画:一种补间动画。通过动态地改变对象的属性从而达到动画效果。属性动画为API 11(Android3.0)的新特性。

一、逐帧动画

简介:帧动画是顺序播放一组预先定义好的图片,类似于电影播放。系统提供了一个类AnimationDrawable来使用帧动画。

1.1 简单使用

效果:

1.1.1 准备图片资源

这里准备三张图片,模拟小喇叭播放的效果。

1.1.1 通过XML来定义一个AnimationDrawable

在应用程序的res/drawable文件夹下将动画Drawable资源定义为外部资源。

文件位置及名称://res/drawable/anim_frame.xml

说明:

  1. :标明每一格图像的资源,以及图像显示的时长。标签的顺序就是动画播放的顺序。
  2. drawable:引用图片资源。
  3. duration:表示该图像显示的时长,单位毫秒。

1.1.2 将上述Drawable作为View的背景并通过AnimationDrawable播放动画

注意

  1. 默认为循环播放。
  2. 将资源分配给View当背景的操作应该在onCreate()中完成。
  3. 在此过程中,动画没有与窗口完全关联,所以动画无法开始。通常动画是作为用户动作(例如按下按钮)的结果播放的,或者在onWindowFocusChanged处理程序中播放。

1.2 AnimationDrawable常用方法说明

1.2.1 start()方法

public void start() {}

说明:从第一帧开始动画,必要时循环。如果动画运行,这个方法没有效果。所以,如果动画只循环一遍,此时虽然动画在视觉上不动了,但是动画并没有停止,再次调用动画start()方法之前,必须先调用stop()方法。

1.2.2 stop()方法

public void stop() {}

说明:停止动画并停留在当前帧。如果动画不运行,此方法没有效果。

1.2.3 isRunning()方法

public boolean isRunning() {}

说明:返回动画是否正在运行。

1.2.4 setOneShot()方法

public void setOneShot(boolean oneShot) {}

说明:设置动画是否只播放一次。

1.2.5 isOneShot()方法

public boolean isOneShot() {}

说明:如果动画只播放一次则返回true。

1.2.6 getDuration()方法

public int getDuration(int i) {}

说明:返回指定位置的帧的播放时长,单位毫秒,从0开始。
注意:参数i可能会引起数组下标越界。

1.2.7 addFrame()方法

public void addFrame(@NonNull Drawable frame, int duration) {}

说明:添加一帧。

1.2.8 getFrame()方法

public Drawable getFrame(int index) {}

说明:返回指定位置的帧的Drawable,从0开始。
注意:参数i可能会引起数组下标越界。

1.2.9 setVisible()方法

public boolean setVisible(boolean visible, boolean restart) {}

说明:设置动画是否可见。当动画不可见时,将暂停动画。后续动画变为可见时,如果restart==true,动画将会从第一帧开始播放;如果restart==false,动画将会从停止的帧开始播放,如果Drawable已经是最后一帧且只播放一次,View会停留在最后一帧。

1.2.10 getNumberOfFrames()方法

public int getNumberOfFrames() {}

说明:返回动画的帧数。

1.3 逐帧动画注意事项

如果图片过多过大就会导致OOM。所以在使用逐帧动画时应尽量避免使用过多尺寸较大的图片。


二、视图动画

简介:视图动画的作用对象是View,它支持4中动画效果,分别是平移动画、缩放动画、旋转动画和透明度动画。这四种动画即可以通过XML定义,也可以通过代码动态创建。建议采用XML来定义,因为XML格式可读性更好

2.1 xml简单实现

2.1.1 xml

在应用程序的res/anim文件夹新建文件xml文件。

文件位置及名称://res/drawable/*.xml

2.1.2 代码应用动画

2.2 xml标签说明

2.2.1 set 标签

简介:标签表示一个动画集合。一个动画集合包含一个或多个动画变换。一个动画集合内部可以嵌套其他动画集合。

属性含义:

  • interpolator:表示动画集合所采用的插值器,用来设置这种效果随时间改变的速度。这个属性可以不指定,默认为加减速插值器(@android:anim/accelerate_decelerate_interpolator)。后文有系统提供的插值器类型说明。
  • shareInterpolator:表示集合中的动画是否和集合共享同一个插值器。如果集合不指定插值器,那么子动画就需要单独制定所需的插值器或者使用默认值。
  • duration:动画的持续时间,以毫秒为单位。
  • startOffset:动画开始之前的延迟,以毫秒为单位。注意:如果子动画没有设置startOffset属性,动画集中的所有动画效果将会同时执行。
  • fillAfter:动画结束以后View是否停留在结束位置。
  • fillBefore:动画结束以后View是否停留在开始位置。默认动画结束以后停留在开始位置。
  • repeatMode:有两个选项:”repeat”和”reverse”,分别表示正向重复和逆向重复。正向重复是指动画每次都重新开始播放。逆向重复是指第一次动画从头放到尾,第二次动画从尾放到头,第三次动画再从头放到尾,第四次再从尾放到头,如此反复。

注意

  1. set 标签的设置的属性值(不包含默认值)优先于子动画设置的属性值。例如:set 标签设置duration属性后,子动画内设置的duration属性会失效。
  2. set 标签的一些属性会全部应用于子动画:duration、repeatMode、fillBefore、fillAfter。
  3. set 标签会忽略一些属性:repeatCount, fillEnabled。(设置了也不起作用)
  4. set 标签一些属性仅作用于本身:startOffset、shareInterpolator。

示例:

1.插值器采用线性插值器(匀速变化);2.子动画共享插值器;3.动画结束后View停留在结束位置;4.子动画重复模式为逆向重复;5.动画持续时间1000毫秒。

2.2.2 translate 标签

简介:表示平移动画,可以使一个View在水平和竖直方向完成平移动画效果。

属性含义:

  1. fromXDelta:x的起始位置,例如 0 。
  2. toXDelta:x的结束位置,例如 200 。
  3. fromYDelta:y的起始值。
  4. toYDelta:y的结束值。

备注:涉及到坐标的属性的值的类型均一下三种。

  1. android:toXDelta=”100%”,表示View自身的100%。
  2. android:toXDelta=”80%p”,表示父层View的80%,是以它父层View为参照的。
  3. android:toXDelta=”100”,表示绝对值。

示例:

备注:没有设置重复哦。

2.2.3 scale 标签

简介:表示缩放动画,可以使View具有放大或缩小的动画效果。

属性含义:

  1. fromXScale:水平方向缩放的起始值,例如 0,5 ,表示View自身大小的0.5 。
  2. toXScale:水平方向缩放的结束值,例如 100dp 。
  3. fromYScale:竖直方向缩放的起始值。
  4. toYScale:竖直方向缩放的结束值。
  5. pivotX:缩放的轴点的x坐标,它会影响缩放的效果。范围:0% ~ 100% 。
  6. pivotY:缩放的轴点的y坐标,它会影响缩放的效果。范围:0% ~ 100% 。

轴点:默认情况下轴点是View的中心点,这个时候在水平方向进行缩放的话会导致View向左右两个方向同时进行缩放,但是如果把轴点设置为View的右边界,那么View就只会向左边进行缩放,反之则向右边进行缩放。

示例:

2.2.4 rotate 标签

简介:表示旋转动画,可以使View具有旋转效果。

属性含义:

  1. fromDegrees:旋转开始的角度,例如 -270.8 。
  2. toDegrees:旋转结束的角度,例如 520.5 。
  3. pivotX:旋转的轴点的x坐标。
  4. pivotY:旋转的轴点的y坐标。

示例:

备注:repeatCount属性需要手敲,studio不会提示。repeatCount=”1”的含义为动画播放完成后,再重复一次,即播放两次。repeatCount=”-1”表示无限循环。

备注:set 标签设置了android:repeatMode=”reverse”。

2.2.5 alpha 标签

简介:表示透明度动画,可以改变View的透明度

属性含义:

  1. fromAlpha:透明度的起始值,范围:[0,1]浮点数。
  2. toAlpha:透明度的结束值,范围:[0,1]浮点数。

示例:

2.3 代码简单实现

效果:

清除动画可以使用:

2.4 系统提供的插值器说明

  1. AccelerateDecelerateInterpolator:先加速,然后减速。
  2. AccelerateInterpolator:加速。
  3. DecelerateInterpolator:减速。
  4. LinearInterpolator:线性变换,即匀速。
  5. AnticipateInterpolator:开始时向后,然后再向前急冲。
  6. AnticipateOvershootInterpolator:开始时向后,然后向前急冲一定的值,最后回到最终的值。
  7. OvershootInterpolator:开始时向前急冲,超过终点值,然后再回来。
  8. BounceInterpolator:动画结束时弹起。

2.5 视图动画过程监听

2.5.1 监听器方法

public static interface AnimationListener {        // 当动画开始的时候,执行该事件里的逻辑        void onAnimationStart(Animation animation);        // 当动画开始的时候,执行该事件里的逻辑        void onAnimationEnd(Animation animation);        // 重复        void onAnimationRepeat(Animation animation);    }

2.5.2 示例

模拟唱片动画。

效果:

页面元素:

  1. 播放按钮
  2. 拨杆
  3. 盘片

实现步骤:

  1. 点击播放按钮:隐藏播放按钮,播放拨杆进动画。
  2. 拨杆进动画结束时,启动盘片旋转动画。
  3. 盘片转动动画结束时,启动拨杆出动画。
  4. 拨杆出动画结束时,显示播放按钮。

2.5.2.1 播放按钮drawable文件

文件://res/drawable/play_button_icon.xml

图片资源:


2.5.2.2 拨杆进动画xml文件

文件://res/anim/rotate_45.xml

2.5.2.3 盘片旋转动画xml文件

文件://res/anim/rotate.xml

2.5.2.4 拨杆出动画xml文件

文件://res/anim/rotate_d_45.xml

2.5.2.5 布局文件

    <FrameLayout        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerInParent="true">        <ImageView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:src="@mipmap/view_center" />        <ImageView            android:id="@+id/view_pan"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="center_vertical"            android:layout_marginLeft="20.5dp"            android:src="@mipmap/view_disc" />        <ImageView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="center_vertical"            android:layout_marginLeft="20.5dp"            android:src="@mipmap/view_disc_light" />        <ImageView            android:id="@+id/view_bar"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginLeft="214dp"            android:src="@mipmap/view_pan" />        <ImageButton            android:id="@+id/view_play_start"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="center_vertical"            android:layout_marginLeft="71dp"            android:background="@drawable/play_button_icon" />    </FrameLayout>

图片资源:




2.5.2.6 代码

private Context mContext = this;private Animation mBarInAnim; //拨杆进动画private Animation mPanAnim; //盘片相关动画private Animation mBarOutAnim; //拨杆出动画private ImageView mViewPan; //盘片控件private ImageView mViewPanBar; //拨杆控件private ImageButton mBtnPlayStart; //Play按键处理@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_animation_view2);    mViewPan = (ImageView) findViewById(R.id.view_pan);    mViewPanBar = (ImageView) findViewById(R.id.view_bar);    mBtnPlayStart = (ImageButton) findViewById(R.id.view_play_start);    //动画    mBarInAnim = AnimationUtils.loadAnimation(mContext,R.anim.rotate_45); //拨杆进动画    mPanAnim = AnimationUtils.loadAnimation(mContext,R.anim.rotate); //盘片相关动画    mBarOutAnim = AnimationUtils.loadAnimation(mContext,R.anim.rotate_d_45); //拨杆出动画    //动画监听器    mBarInAnim.setAnimationListener(new Animation.AnimationListener() {        @Override        public void onAnimationStart(Animation animation) {}        //拨杆进动画结束时        @Override        public void onAnimationEnd(Animation animation) {        mViewPan.startAnimation(mPanAnim);//启动盘片动画        }        @Override        public void onAnimationRepeat(Animation animation) {}    });    mPanAnim.setAnimationListener(new Animation.AnimationListener() {        @Override        public void onAnimationStart(Animation animation) {}        //盘片动画结束时        @Override        public void onAnimationEnd(Animation animation) {        mViewPanBar.startAnimation(mBarOutAnim);//启动拨杆出动画        }        @Override        public void onAnimationRepeat(Animation animation) {}    });    mBarOutAnim.setAnimationListener(new Animation.AnimationListener() {        @Override        public void onAnimationStart(Animation animation) {}        //拨杆出动画结束时        @Override        public void onAnimationEnd(Animation animation) {        mBtnPlayStart.setVisibility(View.VISIBLE);//设置播放按钮为可见        }        @Override        public void onAnimationRepeat(Animation animation) {}    });    //播放按钮点击事件    mBtnPlayStart.setOnClickListener(new View.OnClickListener() {        @Override        public void onClick(View v) {        mBtnPlayStart.setVisibility(View.INVISIBLE);//设置播放按钮为不可见        mViewPanBar.startAnimation(mBarInAnim);        }    });}

2.6 自定义视图动画

自定义视图动画只需要集成Animation抽象类,然后重写它的initialize()方法和applyTransformation()方法。在initialize()方法内做一些初始化工作,在applyTransformation()方法中进行矩阵变换。很多时候需要采用Camera来简化矩阵变换过程。自定义视图动画有难度的地方是矩阵变换过程,需要参考矩阵变换的细节。一般在实际开发中很少用到自定义View动画。

示例:

Rotate3dAnimation(来自于Android的ApiDemos),Rotate3dAnimation可以围绕y轴旋转的同时沿z轴平移从而实现一种类似于3D的效果。

import android.view.animation.Animation;import android.view.animation.Transformation;import android.graphics.Camera;import android.graphics.Matrix;public class Rotate3dAnimation extends Animation {    private final float mFromDegrees;    private final float mToDegrees;    private final float mCenterX;    private final float mCenterY;    private final float mDepthZ;    private final boolean mReverse;    private Camera mCamera;    /**     * 在Y轴上创建一个新的3D旋转效果。 旋转角度由fromDegrees和toDegrees定义。旋转效果     * 在二维空间上的轴点由centerX和centerY定义。动画开始时,执行Z轴上的平移变换,平移的     * 距离由depthZ定义。reverse定义动画是否逆向播放。     *     * @param fromDegrees the start angle of the 3D rotation     * @param toDegrees the end angle of the 3D rotation     * @param centerX the X center of the 3D rotation     * @param centerY the Y center of the 3D rotation     * @param depthZ the length of the translation on the z axis     * @param reverse true if the translation should be reversed, false otherwise     */    public Rotate3dAnimation(float fromDegrees, float toDegrees,        float centerX, float centerY, float depthZ, boolean reverse) {    mFromDegrees = fromDegrees;    mToDegrees = toDegrees;    mCenterX = centerX;    mCenterY = centerY;    mDepthZ = depthZ;    mReverse = reverse;    }    @Override    public void initialize(int width, int height, int parentWidth, int parentHeight) {    super.initialize(width, height, parentWidth, parentHeight);    //做初始化工作    mCamera = new Camera();    }    @Override    protected void applyTransformation(float interpolatedTime, Transformation t) {    //进行相应的矩阵变换    final float fromDegrees = mFromDegrees;    float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);    final float centerX = mCenterX;    final float centerY = mCenterY;    final Camera camera = mCamera;    final Matrix matrix = t.getMatrix();    camera.save();    if (mReverse) {        camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);    } else {        camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));    }    camera.rotateY(degrees);    camera.getMatrix(matrix);    camera.restore();    matrix.preTranslate(-centerX, -centerY);    matrix.postTranslate(centerX, centerY);    }}

效果:

2.7 视图动画的特殊使用场景

视图动画还可以在一些特殊的场景下使用,例如在ViewGroup中可以控制子元素的出场效果;在Activity中实现不同的Activity之间的切换效果。

2.7.1 LayoutAnimation

LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,这样当它的子元素出场时都会具有这种动画效果。

效果:

(1) 为子元素指定入场的动画

文件://res/ainm/ainm_item.xml

(2) 定义LayoutAnimation

属性说明:

  1. delay:表示子元素开始动画的时间延迟。例如子元素入场动画的时长为300毫秒,那么0.5表示每个子元素都需要延迟150毫秒才能播放入场动画。简单来说,与动画加载时刻为0点,第一个子元素延迟150毫秒开始播放入场动画,第2个子元素延迟300毫秒开始播放动画,依次类推。
  2. animationOrder:表示子元素动画的顺序,有三个选项:normal、reverse和random。normal:排在去前面的子元素先开始播放动画;reverse:排在后前面的子元素先开始播放动画;random:随机播放入场动画。
  3. animation:为子元素指定具体入场的动画。

(3) 对ViewGroup应用LayoutAnimation

为ViewGroup指定android:layoutAnimation=”“属性。对于ListView的item就具有出场动画了,这种方式适用于所有ViewGroup。

(4) 代码实现

2.7.2 Activity的切换效果

Activity有默认的切换效果,但是这个效果我们是可以自定义,主要用到overridePendingTranslation(int enterAnim,int exitAnim)这个这个方法。

参数含义:

  1. enterAnim:Activity被打开时,所需的动画资源id。
  2. exitAnim:Activity被暂停时,所需的动画资源id。

当启动一个Activity时,可以按照如下方式为其添加自定义的切换效果:

当Activity退出时,也可以为其指定自己的切换效果:

注意:这个方法必须在startActivity(Intent)或者finish()之后被调用才能生效。

效果:

Fragment也可以添加切换动画,通过FragmentTransaction中的setCustomAnimations()方法来添加切换动画。

2.8 视图动画注意事项

  1. 视图动画是对View的影响做动画,并不是真正地改变View的状态,因此有时候会出现动画完成后View无法隐藏的现象,即setVisibility(View.GONE)失效,这个时候只要调用view.clearAnimation()清除View动画即可解决此问题。
  2. 视图动画是对View的影响做动画,因此如果View有单击事件监听的话,尽管View的影像发生了改变,但是在View的原位置仍可触发单击事件,而View的影像的位置不可触发单击事件。而属性动画的单击事件位置为View变换后的位置。

三、属性动画

简介:属性动画可以对任意对象的属性进行动画而不仅仅是View。视图动画改变的是View的影像并没有改变对象本身,而属性动画却直接改变了它所作用的对象的属性。因此,属性动画可以在视觉或者其他方面(例如:上文提到的View的单击事件的位置)修改任意对象的任意属性(当然,这里有一些限制,详见3.5 ),通过使用一个属性动画生成器,在一个给定的时间内使用你选择的差值算法将该属性从一个值转换为另一个值。属性动画实现了一组高效的迭代器,它们根据给定时间内的给定差值轨迹,通过后台的一个定时器进行值得递增或递减。

属性动画默认时间间隔为300毫秒,帧率 帧/10毫秒,只运行一次。

属性动画可以使用代码实现,也可以使用xml实现。在实际开发中建议采用代码来实现,因为通过代码实现比较简单,而且有时候一个属性的起始值是无法提前确定的。例如:让一个Button从屏幕的左边移动到屏幕的右边,由于我么无法预知屏幕的宽度,因此无法将属性动画定义在xml中,这时就必须通过代码来动态创建属性动画。

3.1 代码简单实现

3.1.1 ObjectAnimator

ObjectAnimator类包含有ofFloat、ofInt、ofObject方法,可以很容易地将目标对象的特定属性在指定的值之间进行转换。

ObjectAnimator.ofFloat(Object target, String propertyName,from,to)ObjectAnimator.ofFloat(Object target, String propertyName,to)

示例:改变一个对象的translationY属性,让其沿着Y轴平移一段距离:对象的高度。

3.1.2 ValueAnimator ###

示例:使一个对象的背景颜色在3秒内从灰色变为蓝色,并逆向重复无限循环。

注意:使用属性动画中的无限循环动画时,在Activity退出时应该及时停止,否则将会导致Activity无法释放从而造成内存泄漏。经验证视图动画并不存在此问题。

3.1.3 AnimatorSet

使用AnimatorSet类,可以很容易地创建复杂、互相关联的动画。

示例:5秒内对View的旋转、平移、缩放和透明度都进行了变换。

如果想要向一个动画集中添加一个新动画,可以使用play。这个方法返回一个AnimatorSet.Buillder对象,通过它可以指定相对于其他动画何时播放指定的动画。

    animatorSet = new AnimatorSet();    animatorSet.play(firstAnimatioin).before(currentAnim1);    animatorSet.play(currentAnim1).with(currentAnim2);    animatorSet.play(astAnimatioin).after(currentAnim2);    animatorSet.start();

3.2 xml简单实现

文件位置://res/animator/*.xml

<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android"    android:ordering=["sequentially" | "together"]>    <objectAnimator        android:propertyName="string"        android:duration="int"        android:valueFrom="float | int |color"        android:valueTo="float | int |color"        android:startOffset="int"        android:repeatCount="int"        android:repeatMode=["restart" | "reverse"]        android:valueType=["colorType" | "intType" | "floatType" | "pathType"] />    <animator        android:duration="int"        android:valueFrom="float | int |color"        android:valueTo="float | int |color"        android:startOffset="int"        android:repeatCount="int"        android:repeatMode=["restart" | "reverse"]        android:valueType=["colorType" | "intType" | "floatType" | "pathType"] />    <set>    ···    </set></set>

属性说明:

  1. ordering:sequentially表示动画集和中的子动画按照前后顺序依次播放;together表示子动画同时播放。
  2. propertyName:属性动画的作用对象的属性的名称。
  3. valueFrom:属性的起始值。
  4. valueTo:属性的结束值。
  5. valueType:表示android:propertyName所指定的属性的类型。如果android:propertyName所指定的属性表示的是颜色,那么不需要指定android:valueType,系统会自动对颜色属性的类型做处理。

示例:

文件: //res/animator/property_animator.xml

3.3 属性动画的监听器

3.3.1 AnimatorListener

系统还提供了AnimatorListenerAdapter类,它是AnimatorListener的适配器,可以有选择的实现上面的4个方法。

3.3.2 AnimatorUpdateListener

AnimatorUpdateListener会监听整个动画过程,动画是由许多帧组成的,每播放一帧,onAnimationUpdate就会被执行一次。

示例:利用AnimatorUpdateListener,保存动画信息,实现旋转动画的暂停与继续播放。

ImageView mIvGear;private ObjectAnimator objAnimator;private float currentValue = 0;//记录旋转角度/** * 停止动画 * */private void stopAnimation() {    objAnimator.end();    mIvGear.clearAnimation();    currentValue = 0;// 重置起始位置}/** * 暂停动画 */private void pauseAnimation() {    objAnimator.cancel();    mIvGear.clearAnimation();// 清除此ImageView身上的动画}/** * 播放动画 */private void playAnimation(){    objAnimator = ObjectAnimator.ofFloat(mIvGear, "rotation", currentValue - 360, currentValue);    objAnimator.setRepeatCount(ObjectAnimator.INFINITE);    objAnimator.setInterpolator(new LinearInterpolator());    objAnimator.setDuration(6 * 1000);    objAnimator.start();    objAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {        @Override        public void onAnimationUpdate(ValueAnimator animation) {            // 监听动画执行的位置,以便下次开始时,从当前位置开始            // animation.getAnimatedValue()为flort类型            currentValue = (float) animation.getAnimatedValue();        }    });}@Overridepublic void onDetachedFromWindow() {    super.onDetachedFromWindow();    // 控件被移除时,取消动画    if(objAnimator != null){        objAnimator.cancel();    }    mIvGear.clearAnimation();// 清除此ImageView身上的动画}

3.4 插值器和估值器

  • TimeInterpolator:时间插值器,它的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比。
  • TypeEvaluator:类型估值算法,也叫估值器,它的作用是根据当前属性的百分比来计算改变后的属性值,系统预置有IntEvaluator(针对整型属性)、FloatEvaluator(针对浮点型属性)和ArgbEvaluator(针对Color属性)。

IntEvaluator源码:

public class IntEvaluator implements TypeEvaluator<Integer> {    /*     * @param fraction  估值小数(当前属性的百分比)     * @param startValue 开始值     * @param endValue   结束值     * @return 改变后的属性值    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {        int startInt = startValue;        return (int)(startInt + fraction * (endValue - startInt));    }}

自定义插值器需要实现Interpolator或者TimeInterpolator,自定义估值算法需要实现TypeEvaluator。

3.5 对任意属性做动画

属性动画原理:属性动画要求动画作用的对象提供该属性的get和set方法。属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值都不一样,确切来说是随着时间的推移,所传递的值越来越接近最终值。对object的属性 a 做动画,如果想让动画生效,要同时满足两个条件:

  1. object必须提供setA()方法,如果动画的时候没有传递初始值,那么还需要提供getA()方法,因为系统要去取 a 属性的初始值(如果该条件不满足,程序直接Crash)。
  2. object的setA()对属性 a 所做的改变必须能够通过某种方法反映出来,比如会带来UI的改变之类的(如果这条不满足,动画无效果但不会Crash)。

例如:给Button加一个动画,让这个Button的宽度从当前宽度增加到500px。会发现,对Button的width属性做动画却没有效果。这是因为Button内部虽然提供了getWidth()和setWidth()方法,但是通过查看源码可以得知,setWidth()方法设置的是android:width属性,设置的是最大宽度和最小宽度,并不是View的宽度。对应属性动画的两个条件来说,只满足了条件1而未满足条件2,故而动画没有效果。

解决方案:

  1. 用一个类包装原始对象,间接为其提供get和set方法。
  2. 采用ValueAnimator,监听动画过程,自己实现属性的改变。

1. 用一个类包装原始对象,间接为其提供get和set方法

private void performAnimate() {    ViewWrapper wrapper = new ViewWrapper(mVBtn);    ObjectAnimator.ofInt(wrapper,"width",500).setDuration(2000).start();}private static class ViewWrapper{    private View mTarget;    public ViewWrapper(View target){        mTarget = target;    }    public int getWidth(){        return mTarget.getLayoutParams().width;    }    public void setWidth(int width){        mTarget.getLayoutParams().width = width;        mTarget.requestLayout();    }}

上述代码在2S内让Button的宽度增加到了500px。这个提供了ViewWrapper类专门用于包装View,然后对ViewWrapper的width属性做动画,并且在setWidth方法中修改其内部的target的宽度。

2. 采用ValueAnimator,监听动画过程,自己实现属性的改变

 void performAnimate(final View target,final int start,final int end){        ValueAnimator valueAnimator = ValueAnimator.ofInt(1,100);        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            private IntEvaluator mEvaluator = new IntEvaluator();            @Override            public void onAnimationUpdate(ValueAnimator animator) {                //获取当前进度占整个动画过程的比例,浮点型,0~1之间                float fraction = animator.getAnimatedFraction();                //直接调用整型估值器,用过比例计算出宽度,然后再设给Button                target.getLayoutParams().width = mEvaluator.evaluate(fraction,start,end);                target.requestLayout();            }        });        valueAnimator.setDuration(2000).start();}

上述代码在2S内将1个数从1变到100,然后动画的每一帧会回调onAnimationUpdate方法。在这个方法里,我们可以获取到估值小数,从而计算出Button当前的宽度。


四、使用动画的注意事项

  1. 兼容性问题:动画再3.0以下的系统上有兼容性问题,在某些特殊的场景可能无法正常工作。
  2. 不要使用px
  3. 硬件加速:使用动画的过程中,建议开启硬件加速,这样会提高动画的流畅性。
原创粉丝点击