《Android 开发艺术探索》学习笔记之Android动画深入分析

Android的动画有三种:View动画、帧动画和属性动画

一、View动画

View动画是对View的影像做改变,并不是真的改变View的状态

1、View动画的种类
  • 共有四种,分别对应着Animation的四个子类及XML文件中的四种标签

    名称子类标签
    平移动画TranslateAnimation<translate>
    缩放动画ScaleAnimation<scale>
    旋转动画RotateAnimation<rotate>
    透明度动画AlphaAnimation<alpha>
  • XML文件的位置

    • res/anim/filename.xml

      <?xml version="1.0" encoding="utf-8"?>
      <set xmlns:android="http://schemas.android.com/apk/res/android" 
          android:interpolator="@android:anim/accelerate_decelerate_interpolator"
          android:shareInterpolator="true" >
      
          <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>
    • android:interpolator=”@android:anim/accelerate_decelerate_interpolator”

      • 表示动画集合所采用的插值器。
      • 用于控制动画的速度。
      • 可以不指定,默认为加速减速插值器
    • android:shareInterpolator=”true”
      • 表示集合中的动画是否和集合共享同一个插值器
      • 如果集合不指定插值器,那么子动画就要单独指定或使用默认值
    • <alpha>
      • android:fromAlpha:表示透明度的起始值,eg:0.1
      • android:toAlpha:表示透明度的结束值,eg:1
    • <scale>
      • android:fromXScale:水平方向的缩放起始值,比如0.5
      • android:toXScale:水平方向缩放的结束值,比如1.2
      • android:fromYScale:竖直方向的缩放起始值
      • android:toYScale:竖直方向缩放的结束值
      • android:pivotX:缩放的轴点的x坐标,会影响缩放的效果(默认轴点为View的中心)
      • android:pivotY:缩放的轴点的y坐标,会影响缩放的效果(默认轴点为View的中心)
    • <translate>
      • android:fromXDelta:表示x方向平移的起始值,比如0
      • android:toXDelta:表示x方向平移的结束值,比如100
      • android:fromYDelta:表示y方向平移的起始值
      • android:toYDelta:表示y方向平移的结束值
    • <rotate>

      • android:fromDegrees:旋转开始的角度,比如0
      • android:toDegrees:旋转结束的角度,比如180
      • android:pivotX:旋转的轴点的x坐标(默认轴点为View的中心)
      • android:pivotY:旋转的轴点的y坐标(默认轴点为View的中心)
    • android:duration:动画的持续时间

    • android:fillAfter:动画结束后View是否停留在结束的位置
  • 另外可以通过实例化对应的动画子类对象来设置动画属性

    //透明度动画
    
    AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
    alphaAnimation.setDuration(300); //设置动画持续时间为300ms
  • 使用

    Button button = (button) findViewById(R.id.button)
    Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_test);
    button.startAnimation(animation);
  • 通过Animaiton的setAnimationListener方法对View动画添加过程监听
    public static interface AnimationListener {
        void onAnimationStart(Animation animation);
        void onAnimationEnd(Animation animation);
        void onAnimationRepeat(Animation animation);
    }
2、自定义View动画

继承Animation抽象类

  • (1)重写 initialize 方法:做一些初始化工作
  • (2)重写 applyTransformation 方法:进行相应的矩阵变换
    • 使用Camera:简化矩阵变换的过程
3、帧动画(Animation Drawable)

顺序播放一组预先定义好的图片

  • 使用:

    • 首先通过xml来定义一个AnimationDrawable,文件位置:res/drawable/filename.xml

      <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
          android:oneshot="false" >
      
          <item
              android:drawable="@drawable/image_people"
              android:duration="500" />
      
          <item
              android:drawable="@drawable/image_nav_header"
              android:duration="500" />
      
          <item
              android:drawable="@drawable/image_white"
              android:duration="500" />
      
      </animation-list>
    • 然后将上述的Drawable文件作为View的背景通过Drawable来播放动画

      button.setBackgroundResource(R.drawable.frame_animation);
      AnimationDrawable drawable = (AnimationDrawable) button.getBackGround();
      drawable.start(); //开始播放
  • 使用帧动画容易引起OOM,尽量避免使用过多尺寸较大的图片

二、View动画的特殊使用场景

1、LayoutAnimation
  • 作用于ViewGroup。为ViewGroup指定一个动画,这样当它的子元素出场时都会具有这种动画效果。
    • eg:使ListView的每个item都以一定的动画出现
  • 使用:

    • ① 定义LayoutAnimation。文件位置:res/anim/filename.xml

      <layoutAnimation xmlns:android="http://schemas.andorid.com/apk/res/android"
          android:delay="0.5"
          android:animationOrder="normal"
          android:animation="@anim/anim_item" />
    • android:delay:子元素开始动画的时间延迟。

      • 所设定的数值 * 动画时间周期 = 延迟时间 - 假设子元素时间周期为300ms,此例中第一个子元素延迟150ms出现,第二个子元素延迟300ms出现,以此类推
    • android:animationOrder:动画入场的顺序
      • normal:顺序入场
      • reverse:逆序入场
      • random:随机入场
    • android:animation:指定子元素具体的入场动画

    • ② 为子元素设置具体的入场动画

    • ③ 为ViewGroup指定 android:layoutAnimation
      • xml的相应ViewGroup标签下指定
      • 通过 LayoutAnimationController 来实现
        ListView listView = (ListView) layout.findViewById(R.id.list);
        Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_item);
        LayoutAnimationController controller = new LayoutAnimationController(animation);
        controller.setDelay(0.5f);
        controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
        listView.setLayoutAnimation(controller);
2、Activity的切换效果
  • overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim):设置Activity开场结束动画
    • 必须在startActivity(intent)之后finish()之后调用才有效
  • 入场指定
    starActivity(intent);
    overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
  • 结束指定
    @Override
    public void finish() {
        super.finish();
        overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
    }
  • Fragment添加动画
    • 使用support-v4
    • 通过 FragmentTransaction的setCustomAnimations()方法 来添加动画

三、属性动画

属性动画要求对象的属性必须要有set方法

1、基本使用
  • 简介:
    • 属性动画是API11之后新加入的特性,通过开源动画库nineoldandorids来兼容以前的版本(API11之前的版本是通过代理View动画实现)
    • 属性动画可以对任意对象的属性进行动画而不仅仅是View
      • 动画默认时间间隔为300ms
      • 动画默认帧率为10ms/帧
    • 常用类有三个
      • (1)ValueAnimator:改变的是值而不一定是具体对象—<animator>
        • (2)ObjectAnimatior:继承自ValueAnimator—<objectAnimator>
      • (3)AnimatorSet:动画集合—<set>
    • 属性动画需要定义在res/animator目录下
  • 通过Java代码定义:(推荐)

    • ObjectAnimator

      //平移
      
      ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight).start();
    • ValueAnimator

      //改变背景色
      
      ValuewAnimator colorAnim = ObjectAnimator.ofInt(this, "backgroundColor", 0*FFFF8080, 0*FF8080FF);
      colorAnim.setDurtion(3000);
      colorAnim.setRepeatCount(ValueAnimator.INFINITE); //设置动画无限次循环
      colorAnim.setRepeatMode(ValueAnimator.REVERSE); //设置反转效果
      corlorAnim.start(); //开始动画
    • AnimatorSet

      AnimatorSet set = new AnimatorSet();
      set.playTogether (
          ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight()).start();
          ObjectAnimator.ofFloat(myObject, "translationX", -myObject.getWidth()).start();
          ···
      );
      set.setDuration(5*1000).start();
  • 通过XML定义:

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:ordering="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="intType | floatType" />
    
        <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="intType | floatType" />
    
        <set
            ···
        </set>
    
    </set>
    • android:ordering:动画集合中的子动画播放顺序
      • together:默认值。同时播放
      • sequentially:顺序播放
    • android:propertyName:表示属性动画作用对象的属性的名称
      • <animator>没有此属性
    • android:duration:表示动画的时长
    • android:valueFrom:表示属性的起始值
    • android:valueTo:表示属性的结束值
    • android:startOffset:表示动画的延迟时间(ms)
    • android:repeatCount:表示动画的重复次数
      • 0:默认值
      • -1:无限次循环
    • android:repeatMode:表示动画的重复模式
      • restart:连续重复
      • reverse:逆向重复(正序、逆序、正序、逆序…)
    • android:valueType:表示属性类型
      • intType:整型类型
      • floatType:浮点型类型
  • 使用:
    AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext, R.anim.test_animator);
    set.setTarget(button);
    set.start();
2、插值器与估值器

实现非匀速动画

  • TimeInterpolator(插值器):跟数据时间流逝的百分比来计算出当前属性值改变的百分比
    • LinearInterpolator:线性插值器,匀速动画。
    • AccelerateDecelerateInterpolator:加速减速插值器,动画两头慢中间快
    • DecelerateInterpolator:减速插值器,动画越来越慢
  • TypeEvaluator(估值器/类型估值算法):根据当前属性改变的百分比来计算改变后的属性值
    • IntEvaluator:针对整型属性
    • FloatEvaluator:针对浮点型属性
  • 自定义
    • 自定义插值器:实现Interpolator或TimeInterpolator接口
    • 自定义估值器:实现TypeEvaluator接口
3、属性动画的监听器
  • AnimatorListener
    • 另外有AnimatorListenerAdapter可以有选择的实现下面的四种方法
    public 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);
    }
4、对任意属性做动画
  • 要求

    • object必须提供setAbc方法
      • 如果动画的时候没有传递初始值,那么还要提供getAbc方法用于获取abc属性的初始值,否则程序直接Crash
    • object的setAbc对属性abc所做的改变必须能够通过某种方法反映出来
  • 可以用一个类来包装原始对象,间接为其提供get和set方法

    private void performAnimate() {
        ViewWrapper wrapper = new ViewWrapper(mButton);
        ObjectAnimator.ofInt(wrapper, "width", 500)。setDuration(5000).start();
    }
    
    @Override
    public void onClick(View v) {
        if (v == mButton) {
            performAnimate();
        }
    }
    
    private static class ViewWrapper {
        private View mTarget;
    
        public ViewWrapper(View target) {
            mTarget = target;
        }
    
        public int getWith() {
            return mTarget.getLayoutParams().width;
        }
    
        public void setWidth(int width) {
            mTarget.getLayuoutParamas().width = width;
            mTarget.requestLayout();
        }
    }
  • 或者可以用ValueAnimator监听动画过程,自己实现属性的改变

    //效果与上一种方法一致
    
    private void performAnimate(final View target, final int start, final int end) {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
        valueAnimator.addUpdateListener(new AnimatorUpdateListener(){
            //持有一个IntEvaluator对象,方便下面估值的时候使用
            private IntEvaluator mEvaluator = new IntEvaluator();
    
            @Override
            public void onAnimationUpdate(ValueAnimator animator) {
                //获取当前动画的进度值,整型,1-100之间
                int currentValue = (Interger) animator.getAnimatedValue();
    
                //获取当前进度占整个动画过程的比例,浮点型,0-1之间
                float fraction = animator.getAnimatedFraction();
                //直接调用整型估值器,通过比例计算出宽度,然后再设给Button
                target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);
                target.requestLayout();
            }
        }); 
    
        valueAnimator.setDuration(5000).start();
    }
    
    @Override
    public void onClick(View v) {
        if (v == mButton) {
            performAnimate(mButton, mButton.getWidth(), 500);
        }
    }
    
5、属性动画的工作原理

属性动画的原理就是根据外部传入的数值多次调用对象属性的set方法。

  • 入口start()
    • 需要运行在有Looper的线程中
    • 依次调用插值器、估值器来设置属性
    • set和get方法均是通过反射来调用的

四、注意事项

  • 无限循环动画要在Activity退出时及时停止,否则会内存泄漏。
  • View动画完成后View无法隐藏(即setVisibility(View.GONE)失效),调用view.clearAnimation()清除
  • 不要使用px
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值