Android动画(一)之View动画

一、概述

Android的动画可以分为三种:View动画、帧动画和属性动画。本文中主要对View动画进行简单的概括与介绍,其实帧动画也属于View动画的一种,只不过它和平移、旋转等常见的View动画在表现形式上略有不同而已。

View动画是通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,它是一种渐进式动画,并且View动画支持自定义

View动画的作用对象是View,它支持4种动画效果,分别是平移动画,缩放动画、旋转动画和透明度动画。上面提到过帧动画也属于View动画,为了更好的区分这四种变换和帧动画,在 本文中如果没有特殊说明,那么所提到的View动画均指这四种变换,帧动画在后面会单独介绍。下面开始正文:

二、View动画的种类

View动画的四种变换效果分别对应着Animation的四个子类:TranslateAnimationScaleAnimationRotateAnimationAlphaAnimation,如下表所示。这四种动画既可以通过XML来定义,也可以通过代码来动态创建,对于View动画来说,建议采用XML来定义动画,这是因为XML格式的动画可读性更好。

名称 标签 子类 效果
平移动画 <translate> TranslateAnimation 移动View
缩放动画 <scale> ScaleAnimation 放大或缩小View
旋转动画 <rotate> RotateAnimation 旋转View
透明度动画 <alpha> AlphaAnimation 改变View的透明度

要使用View动画,首先要创建动画的XML文件,这个文件的路径为:res/anim/filename.xmlView动画的描述文件是有固定语法的,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@[package:]anim/interpolator_resource"
    android:shareInterpolator=["true" | "false"]>


    <alpha 
        android:fromAlpha="float"
        android:toAlpha="float"/>

    <scale 
        android:fromXScale="float"
        android:toXScale="float"
        android:fromYScale="float"
        android:toYScale="float"
        android:pivotX="float"
        android:pivotY="float"/>
    
    <translate 
        android:fromXDelta="float"
        android:toXDelta="float"
        android:fromYDelta="float"
        android:toYDelta="float" />
    
    <rotate 
        android:fromDegrees="float"
        android:toDegrees="float"
        android:pivotX="float"
        android:pivotY="float" />
    
    <set>
        ...
    </set>
</set>

从上面的语法可以看出,View动画既可以是单个动画,也可以由一系列动画组成

<set>标签表示动画集合,对应AnimationSet类,它可以包含若干个动画,并且它的内部也是可以嵌套其他动画集合的,它的两个属性含义如下:

  • android:interpolator
    表示动画集合所采用的的插值器,插值器影响动画的速度,比如非均匀动画就需要通过插值器来控制动画的播放过程。这个属性可以不指定,默认为@android:anim/interpolator_resource,即加速插值器
  • android:shareInterpolator
    表示集合中的动画是否和集合共享同一个插值器。如果集合不指定插值器,那么子动画就需要单独指定插值器或使用默认值。

<translate>标签表示平移动画,对应TranslateAnimation类,它可以使一个View在水平和竖直方向完成平移的动画效果,它的一系列属性的含义如下:

  • android:fromXDelta ---- 表示X的起始值,比如0;
  • android:toXDelta ---- 表示X的结束值,比如100;
  • android:fromYDelta ---- 表示Y的起始值;
  • android:toYDelta ---- 表示Y的结束值;

<scale>标签表示缩放动画,对应ScaleAnimation,它可以使View具有放大或缩小的动画效果,它的一系列属性的含义如下:

  • android:fromXScale ---- 水平方向缩放的起始值,比如0.5;
  • avandroid:toXScale ---- 水平方向缩放的结束值,比如1.2;
  • android:fromYScale ---- 竖直方向缩放的起始值;
  • android:toYScale ---- 竖直方向缩放的结束值;
  • android:pivotX ---- 缩放的轴点的x坐标,它会影响缩放的效果;
  • android:pivotY ---- 缩放的轴点的Y坐标,它会影响缩放的效果;

上面提到了轴点的概念,举个例子,默认情况下轴点是View的中心,这时在水平方向进行缩放的话会导致View向左右两个方向同时进行缩放,但是如果把轴点设为View的右边界,那么View就只会向左边进行缩放,反之则会向右边进行缩放。

<rotate>标签表示旋转动画,对应RotateAnimation,它可以使View具有旋转的动画效果,它的属性的含义如下:

  • android:fromDegrees ---- 旋转开始的角度,比如0;
  • android:toDegrees ---- 旋转结束的角度,比如180;
  • android:pivotX ---- 旋转的轴点的x坐标;
  • android:pivotY ---- 旋转的轴点的y坐标;

在旋转动画中也有轴点的概念,它也会影响旋转的具体效果。在旋转动画中,轴点扮演着旋转轴的角色,即View是围绕着轴点进行旋转的。

<alpha>标签表示透明度动画,对应AlphaAnimation,它可以改变View的透明度,它的属性的含义如下:

  • android:fromAlpha ---- 表示透明度的起始值,比如0.1;
  • android:toAlpha ---- 表示透明度的结束值,比如1;

上面介绍了View动画的XML格式。除了上面介绍的属性以外,View动画还有一些常用的属性,如下所示:

  • android:duration ---- 动画持续时间,单位毫秒;
  • android:fillAfter ---- 动画结束以后View是否停留在结束位置,true表示View停留在结束位置,false则不停留。

下面是一个实际的例子:

// res/anim/view_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true"
    android:zAdjustment="normal">

    <translate
        android:duration="100"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:interpolator="@android:anim/linear_interpolator"
        android:toXDelta="100"
        android:toYDelta="0" />

    <rotate
        android:duration="400"
        android:fromDegrees="0"
        android:toDegrees="90" />

</set>

使用方法:

TextView textView = findViewById(R.id.mTextView);
Animation animation = AnimationUtils.loadAnimation(this,R.anim.view_animation);
textView.startAnimation(animation);

除了在XML中定义动画外,还可以通过代码来应用动画,这里举个例子:

TextView textView = findViewById(R.id.mTextView);
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
alphaAnimation.setDuration(300);
textView.startAnimation(alphaAnimation);

在上面的代码中,创建了一个透明度动画,将一个TextView的透明度在300ms内由0变为1,其他类型的View动画也可以通过代码来创建,这里就不做介绍了。另外,通过AnimationsetAnimationListener方法可以给View动画添加过程监听,接口如下所示。从接口的定义可以很清楚地看出每个方法的含义。

public static interface AnimationListener {
  
    void onAnimationStart(Animation animation);

    void onAnimationEnd(Animation animation);
   
    void onAnimationRepeat(Animation animation);
}

三、自定义View动画

除了系统提供的四种View动画外,我们还可以自定义View动画。自定义动画是一件既简单又复杂的事情,说它简单是因为派生一种新动画只需要继承Animation这个抽象类,然后重写它的initializeapplyTransformation方法,在initialize方法中做一些初始化工作,在applyTransformation中进行响应的矩阵变换即可,很多时候需要采用Camera来简化矩阵变换的过程。说它复杂,是因为定义View动画的过程主要是矩阵变换的过程,而矩阵变换是数学上的概念,如果对这方面的知识不熟悉的话,就会觉得这个过程比较复杂了。

一般来说,在实际开发中很少用到自定义View动画。这里提供一个自定义View动画的例子,这个例子来自于AndroidApiDemos中的一个自定义View动画Rotate3dAnimationRotate3Animation可以围绕y周旋转并且同时沿着z轴平移从而实现一种类似于3D的效果,它的代码如下:

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;

    /**
     * Create a new 3D rotation on the Y axis. The rotation is defined by its
     * start angle and its end angle. Both angles are in degrees. The rotation
     * is performed around a center point on the 2D space,defined by a pair
     * of X and Y coordinates,called centerX and centerY. When the animation
     * starts, a translation on the Z axis (depth) is performed. The length
     * of the translation can be specified, as well as whether the translation
     * should be reversed in time
     *
     * @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 reverse     true if the translation should be reversed,false
     *                    otherwise
     */
    public Rotate3dAnimation(float fromDegrees, float toDegrees,
                             float centerX, float centerY, float depthZ, boolean reverse) {
        this.mFromDegrees = fromDegrees;
        this.mToDegrees = toDegrees;
        this.mCenterX = centerX;
        this.mCenterY = centerY;
        this.mDepthZ = depthZ;
        this.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);

    }
}

四、帧动画

帧动画是顺序播放一组预先定义好的图片,类似电影播放,不同于View动画,系统提供了另一个类AnimationDrawable来使用帧动画。帧动画的使用比较简单,首先需要通过XML来定义一个AnimationDrawable,如下所示:

// res/drawable/frame_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list
    android:oneshot="false"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:drawable="@drawable/image1"
        android:duration="500"/>
    
    <item
        android:drawable="@drawable/image2"
        android:duration="500"/>
    
    <item
        android:drawable="@drawable/image3"
        android:duration="500"/>
</animation-list>

然后将上述的Drawable作为View的背景并通过Drawable来播放动画即可:

TextView textView = findViewById(R.id.mTextView);
textView.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable drawable = (AnimationDrawable) textView.getBackground();
drawable.start();

帧动画的使用比较简单,但是比较容易引起OOM,所以在使用帧动画时应尽量避免使用过多尺寸较大的图片

五、使用动画的注意事项

通过动画可以实现一些比较绚丽的效果,但是在使用过程中,也需要注意一些事情,主要分为下面几类:

  1. OOM问题
    这个问题主要出现在帧动画中,当图片数量较多且图片较大时就极易出现OOM,这在实际的开发中要尤其注意,尽量避免使用帧动画。
  2. 内存泄漏
    在属性动画中有一类无限循环的动画,这类动画需要在Activity退出时及时停止,否则将导致Activity无法释放从而造成内存泄漏,通过验证后发现View动画并不存在此问题。
  3. 兼容性问题
    动画在 3.0 以下的系统上有兼容性问题,在某些特殊场景可能无法正常工作,因此需要做好适配工作。
  4. View动画的问题
    View动画是对View的影像做动画,并不是真正的改变View的状态,因此有时候会出现动画完成后View无法隐藏的现象,即setVisibility(View.GONE)失效了,这个时候只要调用view.clearAnimation()清除View动画即可解决此问题。
  5. 不要使用 px
    在进行动画的过程中,要尽量使用dp,使用px会导致在不同的设备上有不同的效果。
  6. 动画元素的交互
    view移动(平移)后,在Android 3.0以前的系统上,不管是View动画还是属性动画,新位置均无法触发单击事件,同时,老位置仍然可以触发点击事件。尽管View已经在视觉上已经不存在了,将View移回原位置后,原位置的单击事件继续生效。从 3.0开始,属性动画的单击事件触发位置为移动后的位置,但是View动画仍然在原处。
  7. 硬件加速
    使用动画的过程中,建议开启硬件加速,这样会提高动画的流畅性。

相关推荐:Android动画(二)之View动画的特殊使用场景Android动画(三)之插值器和估值器

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容