Android 动画深入分析

Android 动画深入分析.png

7.1 View 动画


View 动画对应这 Animation 的四个子类:

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

<set> 标签表示动画集合,对应 AnimationSet 类,它可以包含若干个动画,并且内部也可以嵌套动画集合。

  • android:interpolator
    表示动画集合采用的插值器,影响动画的速度,可以不指定为匀速。

  • 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;

  • android:toXScale:水平方向缩放的结束值,比如 1.2;

  • android:pivotX:缩放轴点的x坐标,它会影响缩放的效果;

  • android:pivotY:缩放轴点的y坐标,它会影响缩放的效果;

  • android:toXScale:水平方向缩放的结束值,比如1.2;

  • android:toYScale:竖直方向缩放的起始值;

<rotate>标签表示旋转动画,对应于 RotateAnimation,它可以使 View 具有旋转效果。

android:fromDegrees:旋转开始的角度,比如 0;

android:toDegrees:旋转结束的角度,比如180;

android:pivotX:旋转轴点的 x;

android:pivotY:旋转轴点的 y;

<alpha>表示透明动画。对应的 AlphaAnimation。

  • android:fromAlpha:表示透明度的起始值,比如0.1

  • android:toAlpha:表示透明度的结束值,比如1

上面都只是很简单的介绍了XM格式,具体的使用方法还是看文档,我们还有一些常用的属性如下

  • android:duration:动画的时间

  • android:fillAfter:动画结束之后是否停留在结束的位置

演示一个一秒钟平移 100 加旋转 360 度然后复原的案例:

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="false">

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

    <rotate
        android:duration ="1000"
        android:fromDegrees="0"
        android:toDegrees="360"
        />
</set>

再加一个代码写一个 透明动画:

    Animation animation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.my_anim);
    button.startAnimation(animation);
    //代码动画
    AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
    alphaAnimation.setDuration(1000);
    animation.setAnimationListener(new Animation.AnimationListener() {
        //开始
        @Override
        public void onAnimationStart(Animation animation) {}
        //结束
        @Override
        public void onAnimationEnd(Animation animation) {}
        //重复
        @Override
        public void onAnimationRepeat(Animation animation) { }
    });    
    button2.startAnimation(alphaAnimation);
GIF.gif

7.1.2 自定义 View 动画

自定义动画只需要继承 Animation,然后重写它的 initialize 和 applyTransformation 方法

  • initialize:初始化
  • applyTransformation:进行相应的矩阵变换,很多时候需要采用 Camera 来简化矩阵变换的过程。
    GIF.gif

可以参照 ApiDemos 中的 Rotate3dAnimation。

7.1.3 帧动画

帧动画使用很简单,但是比较容易引起 OOM,所以要避免使用大尺寸图片。

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@mipmap/timg" android:duration="500"/>
    <item android:drawable="@mipmap/timgb" android:duration="500"/>
    <item android:drawable="@mipmap/timgc" android:duration="500"/>
</animation-list>
 button.setBackgroundResource(R.drawable.my_list);
 AnimationDrawable background = (AnimationDrawable) button.getBackground();
 background.start();
GIF.gif

7.2 View 动画的特殊使用场景


7.2.1 LayoutAnimation

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

<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:delay="0.5"
    android:animationOrder="normal"
    android:animation="@anim/anim_item">
</layoutAnimation>
  • android:delay
    表示子元素开始动画的延迟,0.5 就是一半的时间周期。

  • android:animationOrder
    表示子元素动画的顺序。

  • normal:顺序显示

  • reverse:逆向显示

  • random:随机显示

  • android:animation
    为子元素指定具体的入场动画。

GIF.gif

先新建一个 set 动画集合,里面是从透明到不透明并且平移的一个效果

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:shareInterpolator="true" >

    <alpha android:fromAlpha="0.0" android:toAlpha="1.0" />

    <translate android:fromXDelta="500" android:toXDelta="0" />

</set>

新建一个 layoutAnimation ,设置显示样式效果

<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
                 android:animation="@anim/anim_item"
                 android:animationOrder="normal"
                 android:delay="0.5">
</layoutAnimation>

在 XML 中指定 LayoutAnimation

<ListView
        android:id="@+id/list_view"
        android:layoutAnimation="@anim/my_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

或者可以通过 LayoutAnimationConreoller 来实现

Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_item);
        LayoutAnimationController controller = new LayoutAnimationController(animation);
        controller.setDelay(0.5f);
        controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
        mListView.setLayoutAnimation(controller);

7.2.2 Activity 的切换效果

Activity 的自定义切换效果。主要用到 overridePendingTransition (int enterAnim,int enterAnim)这个方法。

  • enterAnim: Activity 被打开时,所需要的动画资源 id;
  • exitAnim: Activity 被暂停时,所需要的动画资源 id;
Intent intent = new Intent(MainActivity.this, ListViewActivity.class);
                startActivity(intent);
                overridePendingTransition(R.anim.my_anim,R.anim.my_anim);
@Override
    public void finish() {
        super.finish();
        overridePendingTransition(R.anim.my_anim,R.anim.my_anim);
    }
GIF.gif

7.3 属性动画


7.3.1 使用属性动画

属性动画可以对任意对象进行动画而不仅仅是 View,几乎是无所不能的,只要有对象这个属性。

改变一个对象的 translationX 值。

ObjectAnimator.ofFloat(mButton, "translationX",200).start();

改变一个对象的背景颜色属性。

                ObjectAnimator colorAnim = ObjectAnimator.ofInt(mButton, "backgroundColor", 0xFFFF8080, 0xFF8080FF);
                colorAnim.setDuration(2000);
                colorAnim.setEvaluator(new ArgbEvaluator());
                //INFINITE 表示无限次
                colorAnim.setRepeatCount(ValueAnimator.INFINITE);
                //REVERSE 表示反转效果
                colorAnim.setRepeatMode(ValueAnimator.REVERSE);
                colorAnim.start();

动画集合,5秒内对 View 的旋转、平移、缩放和透明度都进行了改变

AnimatorSet set = new AnimatorSet();
                set.playTogether(
                        ObjectAnimator.ofFloat(mButton,"rotationX",0,360),
                        ObjectAnimator.ofFloat(mButton,"rotationY",0,360),
                        ObjectAnimator.ofFloat(mButton,"rotation",0,-180),
                        ObjectAnimator.ofFloat(mButton,"translationX",0,90),
                        ObjectAnimator.ofFloat(mButton,"translationY",0,90),
                        ObjectAnimator.ofFloat(mButton,"scaleX",1,1.5f),
                        ObjectAnimator.ofFloat(mButton,"scaleY",0,2.5f),
                        ObjectAnimator.ofFloat(mButton,"alpha",1,0.25f,1)
                );
                set.start();

属性动画除了通过代码实现以外,还可以通过 XML 来定义,定义在 res/animator/ 目录下。

<objectAnimator
        android:duration="3000"
        android:propertyName="x"
        android:valueFrom="-50"
        android:valueTo="200"
        android:valueType="floatType" />

    <objectAnimator
        android:propertyName="y"
        android:duration="3000"
        android:valueFrom="1"
        android:valueTo="300"
        android:valueType="floatType" />
  AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(ValueActivity.this, R.animator.my_animator);
                set.setTarget(mButton);
                set.start();
GIF.gif
  • android:propertyName:表示属性动画作用对象的属性的名称

  • android:duration:表示动画的时长

  • android:valueFrom:表示属性的起始值

  • android:valueTo:表示属性的结束值

  • android:startOffset:表示动画的延迟时间,当动画开始后,需要延迟多少毫秒才会真正的播放

  • android:repeatCount:表示动画的重复次数,默认 0,-1 表示无线循环

  • android:repeatMode:表示动画的重复模式

  • restart:表示连续重复

  • reverse:表示连逆向重复,就是反转

  • android:valueType:表示 propertyName 有两个属性有 int 和 float 两个可选项,分别表示属性的类型,和浮点型,另外,如果所制定的是颜色类型,那么就不需要指定 propertyName,系统会自动对颜色类型进行处理

实际开发建议采用代码来实现,简便动态。

7.3.2 理解插值器和估值器

TimeInterpolator:中文翻译是时间插值器的意思,他的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预置的有:

  • LinearInterpolator:(线性加速器,匀速加速器),加速和减速插值器

  • AccelerateDecrateInterpolator:(加速减速插值器:动画两头慢中间快)

  • DecelerateInterpolator:(减速插值器:动画越来越慢)

TypeEvaluator:的中文翻译是类型估值算法,也叫估值器,他的作用是根据当前属性变化的百分比来计算变化后的属性值,系统也预设的有:

  • IntEvaluator:整型
  • FloatEvaluator:浮点型
  • ArgbEvaluator:Color属性

属性动画中的插值器和估值器很重要,他们实现非匀速动画的重要手段。也可以自定义使用。

public class LinearInterpolator implements Interpolator{

        @Override
        public float getInterpolation(float input) {
                if (mFactor == 1.0f) {
                    return input * input;
                } else {
                    return (float)Math.pow(input, 2);

        }
    }
    public class MyEvaluator extends IntEvaluator {

        //fraction 百分比、startValue 开始值、endValue 结束值
        @Override
        public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
            return super.evaluate(fraction, startValue, endValue);
        }
    }

7.3.3 属性动画的监听器

属性动画提供监听器用于监听动画的播放,主要有两个接口:

  • AnimatorListener
public static interface AnimatorListener {
        //开始
        void onAnimationStart(Animator animation);
        //结束
        void onAnimationEnd(Animator animation);
        //取消
        void onAnimationCancel(Animator animation);
        //重复
        void onAnimationRepeat(Animator animation);
    }
  • **AnimatorUpdateListener **
public static interface AnimatorUpdateListener {

        void onAnimationUpdate(ValueAnimator animation);

    }

AnimatorUpdateListener 比较特殊,它会监听整个动画过程的,每播放一帧,就会调用一次 AnimatorUpdateListener。

7.3.4 对任意属性做动画

给 Button 加一个动画,增加 Button 的宽度到 500 px。View 动画只能通过 缩放 Scale 来让 Button 在 x 方向上被放大,而且有可能超过屏幕大小,而属性动画可以并且不会超越屏幕。

GIF.gif

方法 1:直接使用(TextView 和其子类)

ObjectAnimator.ofInt(mButton, "width",50000).setDuration(2000).start();

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

    private void performAnimate() {
        ViewWrapper viewWrapper = new ViewWrapper(mButton);
        ObjectAnimator.ofInt(viewWrapper, "width",500).setDuration(2000).start();
    }

    public 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();
        }
    }
ViewWrapper viewWrapper = new ViewWrapper(mButton);
ObjectAnimator.ofInt(viewWrapper, "width",500).setDuration(2000).start();

方法 3:采用 ValueAnimator,监听动画过程的每一帧,自己实现属性的改变

private void performAnimator(final View target, final int start, final int end) {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            //持有一个IntEvaluator对象,方便下面估值的时候使用
            private IntEvaluator mEvaluator = new IntEvaluator();

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //获得当前动画的进度值,整形1-100之间
                int currentValue = (int) animation.getAnimatedValue();
                //获得当前进度占整个动画之间的比例,浮点0-1之间
                float fraction = animation.getAnimatedFraction();
                //直接使用整形估值器,通过比例计算宽度,然后再设置给 Button
                target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);
                target.requestLayout();
            }
        });
        valueAnimator.setDuration(5000).start();
    }
performAnimate(mButton,mButton.getWidth(),5000);

方法 4:给你的对象加上 get 和 set,如果你有权限的话(一般没有,所以可以不用)

7.3.5 属性动画的工作原理

属性动画要求动画作用的对象提供该属性的 set 方法,属性动画根据那你传递的该属性的初始值和最终值,以动画的效果多次去调用 set 方法,每次传递给 set 方法的值都不一样,确切的来说随着时间的推移,所传递的值越来越接近最终值,如果动画的时候没有传递初始值,那么还要提供 get 方法,因为系统需要获取属性的初始值。这就上面的 方法 2 需要设置 get 和 set 的原因。

一句话:最后是通过反射来调用。(源码不清不楚,先放着)

7.4 使用动画的注意事项

  1. OOM 问题
    这个问题主要出现在帧动画,当图片较多且大就容易出现,这个时候尽量避免使用帧动画。

  2. 内存泄漏
    在属性动画中有设置无线循环的动画,这类动画要在退出时要及时停止,View 动画并不在此问题。

  3. ** 兼容性问题**
    动画在 3.0 以下的系统上有兼容问题,要做好适配工作。

  4. View 动画的问题
    View 动画是对 View 的影像做动画,并不是正真的改变 View 的状态,调用方法不能正常使用时,调用 View.clearAnimation 清除可以解决。

  5. 不要使用 px
    在进行动画的过程中,要使用 dp,用 px 会有适配问题。

  6. 动画元素的交互
    3.0 之前的问题忽略,3.0 以后属性动画的点击事件触发位置为移动后的位置,但是 View 动画任然在原位置。

  7. 硬件加速
    使用动画的过程中,建议开启硬件加速,这样会提供动画的流畅性。

源码Demo 链接

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

推荐阅读更多精彩内容