zoukankan      html  css  js  c++  java
  • Android基础-Android动画总结

    一、动画分类

    总的来说,Android动画可以分为两类,最初的传统动画和Android3.0 之后出现的属性动画;

    传统动画又包括 帧动画(Frame Animation)和补间动画(Tweened Animation)。

     

    二、传统动画

    帧动画

    帧动画是最容易实现的一种动画,这种动画更多的依赖于完善的UI资源,他的原理就是将一张张单独的图片连贯的进行播放,从而在视觉上产生一种动画的效果;有点类似于某些软件制作gif动画的方式。

    如上图中的京东加载动画,代码要做的事情就是把一幅幅的图片按顺序显示,造成动画的视觉效果。

    京东动画实现

    <?xml version="1.0" encoding="utf-8"?>
    <animation-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item
            android:drawable="@drawable/a_0"
            android:duration="100" />
        <item
            android:drawable="@drawable/a_1"
            android:duration="100" />
        <item
            android:drawable="@drawable/a_2"
            android:duration="100" />
    </animation-list>
    protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_frame_animation);
            ImageView animationImg1 = (ImageView) findViewById(R.id.animation1);
            animationImg1.setImageResource(R.drawable.frame_anim1);
            AnimationDrawable animationDrawable1 = (AnimationDrawable) animationImg1.getDrawable();
            animationDrawable1.start();
        }

    可以说,图片资源决定了这种方式可以实现怎样的动画

    在有些代码中,我们还会看到android:oneshot="false" ,这个oneshot 的含义就是动画执行一次(true)还是循环执行多次。

    这里其他几个动画实现方式都是一样,无非就是图片资源的差异。

     

    补间动画

    补间动画又可以分为四种形式,分别是 alpha(淡入淡出),translate(位移),scale(缩放大小),rotate(旋转)。 

    补间动画的实现,一般会采用xml 文件的形式;代码会更容易书写和阅读,同时也更容易复用。

     

    XML实现

    首先,在res/anim/文件夹下定义如下的动画实现方式

    alpha_anim.xml

    <?xml version="1.0" encoding="utf-8"?>
    <alpha xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:fromAlpha="1.0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:toAlpha="0.0" />

    sacle.xml

    <?xml version="1.0" encoding="utf-8"?>
    <scale xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:fromXScale="0.0"
        android:fromYScale="0.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1.0"
        android:toYScale="1.0"/>

    然后,在Activity中

    Animation animation = AnimationUtils.loadAnimation(mContext, R.anim.alpha_anim);
    img = (ImageView) findViewById(R.id.img);
    img.startAnimation(animation);

    这样就可以实现ImageView alpha 透明变化的动画效果。

    也可以使用set 标签将多个动画组合(代码源自Android SDK API)

    <?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>

    可以看到组合动画是可以嵌套使用的。

    各个动画属性的含义结合动画自身的特点应该很好理解,就不一一阐述了;这里主要说一下interpolator 和 pivot。

    Interpolator 主要作用是可以控制动画的变化速率 ,就是动画进行的快慢节奏。

    Android 系统已经为我们提供了一些Interpolator ,比如 accelerate_decelerate_interpolator,accelerate_interpolator等。更多的interpolator 及其含义可以在Android SDK 中查看。同时这个Interpolator也是可以自定义的,这个后面还会提到。

    pivot 决定了当前动画执行的参考位置

    pivot 这个属性主要是在translate 和 scale 动画中,这两种动画都牵扯到view 的“物理位置“发生变化,所以需要一个参考点。而pivotX和pivotY就共同决定了这个点;它的值可以是float或者是百分比数值。

    我们以pivotX为例,

    pivotX取值含义
    10 距离动画所在view自身左边缘10像素
    10% 距离动画所在view自身左边缘 的距离是整个view宽度的10%
    10%p 距离动画所在view父控件左边缘的距离是整个view宽度的10%

    pivotY 也是相同的原理,只不过变成的纵向的位置。如果还是不明白可以参考源码,在Tweened Animation中结合seekbar的滑动观察rotate的变化理解。

     

    Java Code实现

    有时候,动画的属性值可能需要动态的调整,这个时候使用xml 就不合适了,需要使用java代码实现

    private void RotateAnimation() {
            animation = new RotateAnimation(-deValue, deValue, Animation.RELATIVE_TO_SELF,
                    pxValue, Animation.RELATIVE_TO_SELF, pyValue);
            animation.setDuration(timeValue);
    
            if (keep.isChecked()) {
                animation.setFillAfter(true);
            } else {
                animation.setFillAfter(false);
            }
            if (loop.isChecked()) {
                animation.setRepeatCount(-1);
            } else {
                animation.setRepeatCount(0);
            }
    
            if (reverse.isChecked()) {
                animation.setRepeatMode(Animation.REVERSE);
            } else {
                animation.setRepeatMode(Animation.RESTART);
            }
            img.startAnimation(animation);
        }

    这里animation.setFillAfter决定了动画在播放结束时是否保持最终的状态;animation.setRepeatCount和animation.setRepeatMode 决定了动画的重复次数及重复方式,具体细节可查看源码理解。

    好了,传统动画的内容就说到这里了。

     

    三、属性动画

    属性动画,顾名思义它是对于对象属性的动画。因此,所有补间动画的内容,都可以通过属性动画实现。

    属性动画入门

    首先我们来看看如何用属性动画实现上面补间动画的效果

    private void RotateAnimation() {
            ObjectAnimator anim = ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
            anim.setDuration(1000);
            anim.start();
        }
    
        private void AlpahAnimation() {
            ObjectAnimator anim = ObjectAnimator.ofFloat(myView, "alpha", 1.0f, 0.8f, 0.6f, 0.4f, 0.2f, 0.0f);
            anim.setRepeatCount(-1);
            anim.setRepeatMode(ObjectAnimator.REVERSE);
            anim.setDuration(2000);
            anim.start();
        }

    这两个方法用属性动画的方式分别实现了旋转动画和淡入淡出动画,其中setDuration、setRepeatMode及setRepeatCount和补间动画中的概念是一样的。

    可以看到,属性动画貌似强大了许多,实现很方便,同时动画可变化的值也有了更多的选择,动画所能呈现的细节也更多。

    当然属性动画也是可以组合实现的

    ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(myView, "alpha", 1.0f, 0.5f, 0.8f, 1.0f);
    ObjectAnimator scaleXAnim = ObjectAnimator.ofFloat(myView, "scaleX", 0.0f, 1.0f);
    ObjectAnimator scaleYAnim = ObjectAnimator.ofFloat(myView, "scaleY", 0.0f, 2.0f);
    ObjectAnimator rotateAnim = ObjectAnimator.ofFloat(myView, "rotation", 0, 360);
    ObjectAnimator transXAnim = ObjectAnimator.ofFloat(myView, "translationX", 100, 400);
    ObjectAnimator transYAnim = ObjectAnimator.ofFloat(myView, "tranlsationY", 100, 750);
    AnimatorSet set = new AnimatorSet();
    set.playTogether(alphaAnim, scaleXAnim, scaleYAnim, rotateAnim, transXAnim, transYAnim);
    //et.playSequentially(alphaAnim, scaleXAnim, scaleYAnim, rotateAnim, transXAnim, transYAnim);
    set.setDuration(3000);
    set.start();

    可以看到这些动画可以同时播放,或者是按序播放

    属性动画核心原理

    在上面实现属性动画的时候,我们反复的使用到了ObjectAnimator 这个类,这个类继承自ValueAnimator,使用这个类可以对任意对象的任意属性进行动画操作。而ValueAnimator是整个属性动画机制当中最核心的一个类;这点从下面的图片也可以看出。

    属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等。

    从上图我们可以了解到,通

    过duration、startPropertyValue和endPropertyValue 等值,我们就可以定义动画运行时长,初始值和结束值。然后通过start方法开始动画。 那么ValueAnimator 到底是怎样实现从初始值平滑过渡到结束值的呢?这个就是由TypeEvaluator 和TimeInterpolator 共同决定的。

    具体来说,TypeEvaluator 决定了动画如何从初始值过渡到结束值。

    TimeInterpolator 决定了动画从初始值过渡到结束值的节奏。

    说的通俗一点,你每天早晨出门去公司上班,TypeEvaluator决定了你是坐公交、坐地铁还是骑车;而当你决定骑车后,TimeInterpolator决定了你一路上骑行的方式,你可以匀速的一路骑到公司,你也可以前半程骑得飞快,后半程骑得慢悠悠。

    如果,还是不理解,那么就看下面的代码吧。首先看一下下面的这两个gif动画,一个小球在屏幕上以 y=sin(x) 的数学函数轨迹运行,同时小球的颜色和半径也发生着变化,可以发现,两幅图动画变化的节奏也是不一样的。

     

    如果不考虑属性动画,这样的一个动画纯粹的使用Canvas+Handler的方式绘制也是有可能实现的。但是会复杂很多,而且加上各种线程,会带来很多意想不到的问题。

    这里就通过自定义属性动画的方式看看这个动画是如何实现的。

    。。。。。。

    四、传统动画 VS 属性动画

    相较于传统动画,属性动画有很多优势。那是否意味着属性动画可以完全替代传统动画呢。其实不然,两种动画都有各自的优势,属性动画如此强大,也不是没有缺点。

     

     

    • 从上面两幅图比较可以发现,补间动画中,虽然使用translate将图片移动了,但是点击原来的位置,依旧可以发生点击事件,而属性动画却不是。因此我们可以确定,属性动画才是真正的实现了view的移动,补间动画对view的移动更像是在不同地方绘制了一个影子,实际的对象还是处于原来的地方。
    • 当我们把动画的repeatCount设置为无限循环时,如果在Activity退出时没有及时将动画停止,属性动画会导致Activity无法释放而导致内存泄漏,而补间动画却没有问题。因此,使用属性动画时切记在Activity执行 onStop 方法时顺便将动画停止。(对这个怀疑的同学可以自己通过在动画的Update 回调方法打印日志的方式进行验证)。
    • xml 文件实现的补间动画,复用率极高。在Activity切换,窗口弹出时等情景中有着很好的效果。
    • 使用帧动画时需要注意,不要使用过多特别大的图,容易导致内存不足。

     

     

    转载自: https://lrh1993.gitbooks.io/android_interview_guide/content/android/basis/animator.html

     

    很想高飞,但我不能;不想天空,剩我一人。
  • 相关阅读:
    Maven 集成Tomcat插件
    dubbo 序列化 问题 属性值 丢失 ArrayList 解决
    docker 中安装 FastDFS 总结
    docker 从容器中拷文件到宿主机器中
    db2 相关命令
    Webphere WAS 启动
    CKEDITOR 4.6.X 版本 插件 弹出对话框 Dialog中 表格 Table 自定义样式Style 问题
    SpringMVC JSONP JSON支持
    CKEDITOR 3.4.2中 按钮事件中 动态改变图标和title 获取按钮
    git回退到远程某个版本
  • 原文地址:https://www.cnblogs.com/lixiansheng/p/11359933.html
Copyright © 2011-2022 走看看