【总结】安卓动画

本篇为对android动画机制的粗略总结,目的是方便查询而非讲解,所以代码部分的注释比较多,但是很多该配图的地方没有配。
安卓的动画是一种比较基本的特效,项目中经常会用到
安卓的动画分为三种,分别是View动画,帧动画,属性动画。

View动画

View动画的重点是动画,是视图(View)层面上的一种变换,操作的重点在视图上,这与属性动画是完全不同的。
View动画,也有叫补间动画的,就是Flash中的那个补间动画,给定初始值跟结束值,自动计算出过程的一种动画形式。
我个人比较喜欢的用法是用XML文件写动画,然后用代码加载使用,这样的动画可读性非常好。
view动画的所属文件夹为res/anim文件夹



    

    

    

    

    

    

    

    

    

    
        
    

使用的时候加载动画并且执行就好,非常简单。

Animation anim = AnimationUtils.loadAnimation(this, R.anim.anim_view_anim);
View.startAnimation(anim);

【此处假装有动图】

有时候有的属性没有自动提示,敲出不报错就没问题,
例如android:interpolator="@android:anim/cycle_interpolator"有时候编译器不会提示
插值器,估值器的概念,下文会说。

实现一个动画,除了使用xml意外,还可以用代码来写,当动画的某些参数需要动态调整的时候需要这么使用。但写起来比较繁琐,看起来比较乱,所以就只放一个例子,使用方法相同。

    public void translate(View v) {
        // 创建位移补间动画
        // ta = new TranslateAnimation(-100, 100, -50, 50);
        ta = new TranslateAnimation(Animation.RELATIVE_TO_SELF, -2,
                Animation.RELATIVE_TO_SELF, 3, Animation.RELATIVE_TO_SELF, 0,
                Animation.RELATIVE_TO_SELF, 0);
        // 指定动画持续时间
        ta.setDuration(2000);
        // 设置重复播放的次数
        ta.setRepeatCount(1);
        // 设置重复播放的模式
        ta.setRepeatMode(Animation.REVERSE);
        // 填充动画在结束的位置上
        ta.setFillAfter(true);
        // 播放动画
        iv.startAnimation(ta);
    }

View动画也可以自定义,通过继承Animation就可以实现,只不过由于动画实质上是一种数学变换过程,实现起来比较麻烦。项目中系统自带的几种动画组合就能解决大部分问题,所以自定义View动画不常用。

View动画的其他用法

View动画还可以用于设定子元素的出场效果,以及Activity的切换效果。

LayoutAnimation

LayoutAnimation作用于ViewGroup,为其制定一个动画,当它的子元素初始化时,会带有这个效果,常用在RecycleView上。

首先,我们需要一个layoutAnimation



    

然后我们需要一个视图动画



    

    

最后我们为RecycleView指定这个入场动画

        

入场动画也可以为其他ViewGroup指定,效果略有差异。

Activity的切换效果
overridePendingTransition(R.anim.anim1, R.anim.anim2);
// anim1表示入场的Activity执行的动画
// anim2表示退场的Activity执行的动画

需要注意的是,这个动画需要放到startActivity或者finish语句的后面,才会生效。

帧动画

帧动画就是按照既定顺序去播放一组图片的动画效果,类似于电影胶片那种。
帧动画比较简单,效果也比较直观,但是缺点也是显而易见的,过多过大的图片会急剧增大app体积,操作不当还很容易OOM。
适用范围一般,但是很好用的一种动画效果,比如在下拉刷新时,表头部分的动态小图片。



    
    

帧动画资源文件存在于drawable文件夹中,使用时候,获取对象并播放即可。

btn_anim.setBackgroundResource(R.drawable.anim_frame);
        final AnimationDrawable drawable = (AnimationDrawable) btn_anim.getBackground();
        btn_anim.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                drawable.start();
            }
        });

可以在代码中设置背景,也可以在XML中设置。

属性动画

属性动画,理解为在一个时间间隔内,完成对象从一个属性值到另一个属性值的转变。
属性动画的重点在属性,是对对象的属性的一种变换,操作重点在于数值变换,这与View动画是完全不同的。
在看属性动画之前,请先暂时忘记View动画以及帧动画,因为他们不是一个层面上的东西。不能惯性的将属性动画理解为,对View动画的扩展,这样理解出来的属性动画是非常局限的,因为他们虽然同为动画,但是实现原理是完全不同的,使用属性动画可以做到很多View动画做不到的有趣的特效。
属性动画曾经有着严重的兼容问题,但现在app一般要求API16起步,这个问题早已不复存在。
属性动画也可以选用xml的方式或者代码的方式来实现,使用场景类似于View动画。

属性动画的所属文件夹为res/animator文件夹,注意,这与View动画不是同一个文件夹,并且anim文件夹也不是animator的简写。
以下为xml构建属性动画时,可能会用到的属性。



    

    

    

    
    
    
        ...
    

可以看到,属性动画有两个标签
代表ObjectAnimator
代表ValueAnimator
ObjectAnimator与ValueAnimator的关系是继承的关系。
ObjectAnimator类作为ValueAnimator的子类,封装了一些方便开发人员的方法,简化了属性动画的实现。
ObjectAnimator类一般用于实现一些常见的动画效果,例如旋转评议缩放透明度等,ValueAnimator通过ValueAnimator.AnimatorUpdateListener接口,以及估值器,插值器,可以实现一些更加自由的效果。

ObjectAnimator

使用xml构建一个属性动画的步骤与View动画的步骤相似,以下是属性动画的xml文件。



---
Animator animator = AnimatorInflater.loadAnimator(mContext, R.animator.anim_object_anim);
//用objectAnimator也可以只不过需要强转
animator.setTarget(tv_text);
animator.start();

或者是使用set集合的方式。



    
    

---
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(mContext, R.animator.anim_object_anim);
set.setTarget(tv_text);
set.start();

再次强调,如果xml文件中,propertyName 与valueType类型不匹配,可能会出现动画无法执行的情况。

或者也可以用代码的方式实现一个属性动画。

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tv_text, "x", 200);
objectAnimator.setDuration(3000);
objectAnimator.start();

ObjectAnimator中,可用的propertyName有以下几个:
translationXtranslationY,控制位移
xy,控制位移
rotationrotationXrotationY,控制旋转角度
scaleXscaleY,控制缩放
pivotXpivotY,控制以上旋转,缩放的中心点
alpha,控制透明度

以及详细说明:

  1. translationX、translationY,x、y:
    这四个属性作为一组来说明,x,y,是View左上角的坐标,其中,x = left + translationX,y = right + translationY。left,right指的是做动画的View自身的layout_marginLeft,layout_marginRight。
    修改任何一个属性都会使得控件位移,但是在移动细节上稍有差异。
    实例代码以及图1:
    ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(ll_text1, "translationX", 0);
    objectAnimator1.setDuration(3000);
    objectAnimator1.start();
    ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(ll_text2, "x", 0);
    objectAnimator2.setDuration(3000);
    objectAnimator2.start();

【总结】安卓动画_第1张图片
图1 translationX与X.png

可以看到,对控件的 x属性做动画的话,会忽略其自身的margin值

  1. rotation、rotationX、rotationY
    这三个属性作为一组来说明,他们可以控制旋转角度
    其中rotation控制着 2D 旋转角度
    rotationXrotationY控制围绕某枢轴点的 3D旋转
    由于不好描述,放一个图,图2

    【总结】安卓动画_第2张图片
    图2 rotation与rotationX.png

    其中,text1为rotation旋转,text2为rotationX旋转。

  2. scaleX、scaleY
    这两个属性控制着 View 围绕某枢轴点的 2D 缩放比例,这没什么好说的,就是常规缩放。

  3. pivotX、pivotY
    这两个属性控制着上文中,旋转缩放的枢轴点的位置,缺省的枢轴点是控件的中心点

  4. alpha
    这个属性表示控件的透明度。缺省值为 1 (不透明),为 0 则表示完全透明(看不见)

插值器与估值器

在ValueAnimator之前,先要说明插值器与估值器的概念,插值器的概念由于比较常见,不做过多说明也不影响理解,但是估值器的概念不很常见,所以放在这个位置一并说明。

TimeInterpolator,译作插值器,作用是根据时间流逝的百分比来计算出当前值改变的百分比,系统预置有9个插值器,他们的效果如下:

【总结】安卓动画_第3张图片
系统预置插值器

需要注意的是,插值器的特性,使得动画计算的终点与起点有机会相同,如使用CycleInterpolator插值器或者自定义三角函数差值器,当然这也是一个很好用的特性,使用时请保持想象力。

自定义插值器需要继承Interpolator接口,也很简单

public class MyInterpolator implements Interpolator {
    @Override
    public float getInterpolation(float v) {
        return (float) Math.sin(0.5 * Math.PI * v);
    }
}

代码中,return后面公式部分决定了使用该插值器后的运动轨迹,插值器的公式就是简单的二元方程式。
这里提供一个插值器公式的工具网站,http://inloop.github.io/interpolator,是github上的一个项目,建议保存方便以后使用。

插值器使用时,可以直接写在xml中,或者在代码中设置

android:interpolator="@android:anim/decelerate_interpolator"
---
objectAnimator.setInterpolator(new LinearInterpolator());

接下来是估值器
Evaluator,译作估值器,作用是可以用来对int,float,color以外的类型做动画。需要继承TypeEvaluator接口,然后通过该接口来自定义估值算法以及返回值。
以下代码是一个对对象做动画的例子
估值器代码

public class MyTypeEvaluator implements TypeEvaluator {

    @Override
    public TwoNumberBean evaluate(float v, TwoNumberBean startBean, TwoNumberBean endBean) {
        TwoNumberBean bean = new TwoNumberBean();
        bean.number1 = (int) (startBean.number1 + v * (endBean.number1 - startBean.number1));
        bean.number2 = (int) (startBean.number2 + v * (endBean.number2 - startBean.number2));
        return bean;
    }
}

其中,evaluate中,第一个参数表示估值小数,例如总时长1000ms,现在运行了500ms,估值小数的值就是500/1000=0.5。第二、三个参数是开始值以及结束值。

以及使用该估值器的方法

ValueAnimator va = ValueAnimator.ofObject(new MyTypeEvaluator(), new TwoNumberBean(0, 0), new TwoNumberBean(100, 200));
va.setInterpolator(new AccelerateDecelerateInterpolator());
va.setDuration(5000);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator valueAnimator) {
        TwoNumberBean bean = (TwoNumberBean) valueAnimator.getAnimatedValue();
        tv_text1.setText(bean.number1 + "," + bean.number2);
    }
});
va.start();

以及运行效果


【总结】安卓动画_第4张图片
自定义估值器.gif

只是一个示例,估值器同样需要想象力。

ValueAnimator

作为ObjectAnimator的父类,协同插值器,估值器,提供了更加多的可能性,当然也更加复杂。
属性动画提供了两个监听器,用于监听动画的播放过程,主要有两个接口

  1. AnimatorListener监听了动画的开始,结束,取消,重复播放,四个状态,为了方便开发,系统还提供了AnimatorListenerAdapter这个类,毕竟有时候用不到所有方法。
  2. AnimatorUpdateListener监听了整个动画过程,每一帧都会调用一次该方法,比如上面那个例子。

对任意属性做动画

ValueAnimator 最灵活的一点是它可以对任意对象的任意属性做动画。
我们先看一个不是那么好的例子

public class LogBean {
    int i;

    public int getI() {
        Log.e("LogBean: ", "getI:_" + i);
        return i;
    }

    public void setI(int i) {
        Log.e("LogBean: ", "setI:_" + i);
        this.i = i;
    }
}
---
// 对该对象做动画
ObjectAnimator.ofInt(new LogBean(),"i",500).setDuration(5000).start();

然后去看log输出,可以看到LogBean的get、set方法被调用。
记不记得上文说过,“不能惯性的将属性动画理解为,对View动画的扩展”,因为属性动画是以一种更加灵活的方式实现的动画,我曾经因为这个思维惯性,对属性动画的理解偏差很大。
通过这个例子,我们可以得出以下结论:

  1. 被做动画的对象必须要有驼峰命名的get,set方法,否则程序会崩溃。(底层是通过反射实现的)
  2. 属性动画对对象的某个属性值的改变一定要能通过某种方法展现出来,一般开发时,当然是通过改变UI,而这个例子是通过log。
    PS:如果展现不出来不就没有意义了(~ ̄▽ ̄)~

当然,在很多情况下,我们需要对一个对象做动画时,往往不能满足这两个条件,官方文档中有这么三种解决方案。

  1. 如果有权限的话,比如是自己写的对象,可以给这个对象加上get、set方法。不过这个办法一般来说没用。
  2. 使用包装类,为该对象添加get、set方法。
  3. 采用ValueAnimator ,并且通过接口监听动画的整个过程,自己改变属性。这是我最喜欢的一个办法,毕竟灵活度非常高。上文中估值器的例子就是这个方法。

几个需要注意的点

  1. 帧动画的图片不能太大,否则很容易OOM
  2. 如果属性动画设置为无限循环,请在关闭当前Activity时,务必关闭该动画,否则会导致内存泄漏,类似于Handler的内存泄漏。但是View动画并没有这个问题。
  3. View动画只是改变视图,并不会真正改变View的状态。
  4. View动画有时会让view.setVisibility(View.GONE)失效,只要调用view.clearAnimation()清除动画即可。
  5. 可以的话,请开启硬件加速。

个人理解,难免有错误纰漏,欢迎指正。转载请注明出处。

你可能感兴趣的:(【总结】安卓动画)