android动画

镇楼导图

android动画_第1张图片
动画梳理.png

1.帧动画

就是将一张张图片连贯起来播放,使用简单,但是要注意资源图片对内存的占用。

1.1示例



    
    
    
    
    

//使用
ImageView iv_start = findViewById(R.id.iv_start);
iv_start.setImageResource(R.drawable.jd_loading);
AnimationDrawable animationDrawable = (AnimationDrawable) iv_start.getDrawable();
animationDrawable.start();

2 补间动画

共有四种, alpha(淡入淡出),translate(位移),scale(缩放大小),rotate(旋转)。
两种使用方式,xml方式和代码方式
可组合嵌套使用,但行为总有局限
动画本质为影像的绘制,对View本身属性并无改变

2.1 xml使用示例
//单一使用
//组合使用


    
    
    
    
    
        ...
    

//代码操作
Animation animation = AnimationUtils.loadAnimation(this,R.anim.alpha_anim);
iv_start.startAnimation(animation);

pivotX 属性说明
决定了当前动画执行的参考位置
取值及含义如下
10--距离动画所在view自身左边缘10像素
10%--距离动画所在view自身左边缘 的距离是整个view宽度的10%
10%p--距离动画所在view父控件左边缘的距离是整个view宽度的10%

2.2 java code 示例
        Animation rotateAnimation = new RotateAnimation(20,300,0.5f,0.5f);
        Animation alphaAnimation = new AlphaAnimation(0,1);
        Animation translateAnimation = new TranslateAnimation(0,10,0,10);
        Animation scaleAnimation = new ScaleAnimation(0,10,0,10,0.5f,0.5f);

        AnimationSet animationSet = new AnimationSet(true);
        animationSet.addAnimation(rotateAnimation);
        animationSet.addAnimation(alphaAnimation);
        animationSet.addAnimation(translateAnimation);
        animationSet.addAnimation(scaleAnimation);

        animationSet.setFillAfter(true);//动画结束时是否保持最终状态
        animationSet.setRepeatMode(Animation.RESTART);//重复方式
        animationSet.setRepeatCount(-1);//重复次数
        animationSet.setDuration(500);
        iv_start.startAnimation(animationSet);

3 属性动画

3.1 来龙去脉

(1)帧动画和补间动画操作对象仅限于View
(2)帧动画如果图片资源太多较耗内存
(3)补间动画实现的效果仅限于alpha scale translate rotate
(4)补间动画并没有真正的改变属性,例如点击事件不会随位移而变化
(5)为了操作灵活,更好的扩展3.0之后引入属性动画

3.2 属性动画描述

(1)作用对象为java对象,不再仅限于View
(2)操作效果可扩展到任意具有get set方法的属性
(3)属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等。
(4)动画的使用不当可能会引发内存泄露,需要在onStop中取消动画

3.3 ValueAnimator

属性动画的核心类,通过不断控制值的变化,使用时需要手动将变化的值赋值给对象的属性。

常用API
ValueAnimator.ofInt(int values)对应估值器 IntEvaluator
ValueAnimator.ofFloat(float values)对应估值器FloatEvaluator
ValueAnimator.ofObject(int values)需要自己继承TypeEvaluator

3.3.1 java code 示例
//感谢https://www.jianshu.com/p/2412d00a0ce4提供的示例代码,下同
 // 步骤1:设置动画属性的初始值 & 结束值
        ValueAnimator anim = ValueAnimator.ofInt(0, 3);
        // ofInt()作用有两个
        // 1. 创建动画实例
        // 2. 将传入的多个Int参数进行平滑过渡:此处传入0和1,表示将值从0平滑过渡到1
        // 如果传入了3个Int参数 a,b,c ,则是先从a平滑过渡到b,再从b平滑过渡到C,以此类推
        // ValueAnimator.ofInt()内置了整型估值器,直接采用默认的.不需要设置,即默认设置了如何从初始值 过渡到 结束值
        // 下面看看ofInt()的源码分析 ->>关注1
        
// 步骤2:设置动画的播放各种属性
        anim.setDuration(500);
        // 设置动画运行的时长
        anim.setStartDelay(500);
        // 设置动画延迟播放时间
        anim.setRepeatCount(0);
        // 设置动画重复播放次数 = 重放次数+1
        // 动画播放次数 = infinite时,动画无限重复
        anim.setRepeatMode(ValueAnimator.RESTART);
        // 设置重复播放动画模式
        // ValueAnimator.RESTART(默认):正序重放
        // ValueAnimator.REVERSE:倒序回放
// 步骤3:将改变的值手动赋值给对象的属性值:通过动画的更新监听器
        // 设置 值的更新监听器
        // 即:值每次改变、变化一次,该方法就会被调用一次
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 获得改变后的值         
                int currentValue = (Integer) animation.getAnimatedValue();
               // 改变属性
                View.setproperty(currentValue);
              // 刷新视图,即重新绘制,从而实现动画效果
                View.requestLayout();     
            }
        });
// 启动动画
        anim.start();
    }
3.3.2 xml 示例
步骤1:在路径 res/animator的文件夹里创建相应的动画 .xml文件
步骤2:设置动画参数
// ValueAnimator采用  标签
  
步骤3:在Java代码中启动动画
// 载入XML动画
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.set_animation);  
// 设置动画对象
animator.setTarget(view);  
//手动改变属性
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animator) {
                // 获得每次变化后的属性值
                int currentValue = (Integer) animator.getAnimatedValue();
                // 每次值变化时,将值手动赋值给对象的属性
                // 即将每次变化后的值 赋 给按钮的宽度,这样就实现了按钮宽度属性的动态变化
                mButton.getLayoutParams().width = currentValue;
                //刷新视图,即重新绘制,从而实现动画效果
                mButton.requestLayout();
            }
        });
// 启动动画
animator.start();  
3.3.3 ValueAnimator.ofObject 使用
// 创建初始动画时的对象  & 结束动画时的对象
myObject object1 = new myObject();  
myObject object2 = new myObject();  

ValueAnimator anim = ValueAnimator.ofObject(new myObjectEvaluator(), object1, object2);  
// 创建动画对象 & 设置参数
// 参数说明
// 参数1:自定义的估值器对象(TypeEvaluator 类型参数) - 下面会详细介绍
// 参数2:初始动画的对象
// 参数3:结束动画的对象
anim.setDuration(5000);  
anim.start();

ValueAnimator.ofObject(),从上面的工作原理可以看出并没有系统默认实现,因为对对象的动画操作复杂 & 多样,系统无法知道如何从初始对象过度到结束对象
因此,对于ValueAnimator.ofObject(),我们需自定义估值器(TypeEvaluator)来告知系统如何进行从 初始对象 过渡到 结束对象的逻辑
自定义实现的逻辑如下

//定义对象
public class Point {

    // 设置两个变量用于记录坐标的位置
    private float x;
    private float y;

    // 构造方法用于设置坐标
    public Point(float x, float y) {
        this.x = x;
        this.y = y;
    }

    // get方法用于获取坐标
    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }
}

// 实现TypeEvaluator接口
public class PointEvaluator implements TypeEvaluator {

    // 复写evaluate()
    // 在evaluate()里写入对象动画过渡的逻辑
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {

        // 将动画初始值startValue 和 动画结束值endValue 强制类型转换成Point对象
        Point startPoint = (Point) startValue;
        Point endPoint = (Point) endValue;

        // 根据fraction来计算当前动画的x和y的值
        float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
        float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
        
        // 将计算后的坐标封装到一个新的Point对象中并返回
        Point point = new Point(x, y);
        return point;
    }

}
//自定义View
/**
 * Created by Carson_Ho on 17/4/18.
 */
public class MyView extends View {
    // 设置需要用到的变量
    public static final float RADIUS = 70f;// 圆的半径 = 70
    private Point currentPoint;// 当前点坐标
    private Paint mPaint;// 绘图画笔
    

    // 构造方法(初始化画笔)
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 初始化画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.BLUE);
    }

    // 复写onDraw()从而实现绘制逻辑
    // 绘制逻辑:先在初始点画圆,通过监听当前坐标值(currentPoint)的变化,每次变化都调用onDraw()重新绘制圆,从而实现圆的平移动画效果
    @Override
    protected void onDraw(Canvas canvas) {
        // 如果当前点坐标为空(即第一次)
        if (currentPoint == null) {
            currentPoint = new Point(RADIUS, RADIUS);
            // 创建一个点对象(坐标是(70,70))

            // 在该点画一个圆:圆心 = (70,70),半径 = 70
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);


 // (重点关注)将属性动画作用到View中
            // 步骤1:创建初始动画时的对象点  & 结束动画时的对象点
            Point startPoint = new Point(RADIUS, RADIUS);// 初始点为圆心(70,70)
            Point endPoint = new Point(700, 1000);// 结束点为(700,1000)

            // 步骤2:创建动画对象 & 设置初始值 和 结束值
            ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
            // 参数说明
            // 参数1:TypeEvaluator 类型参数 - 使用自定义的PointEvaluator(实现了TypeEvaluator接口)
            // 参数2:初始动画的对象点
            // 参数3:结束动画的对象点

            // 步骤3:设置动画参数
            anim.setDuration(5000);
            // 设置动画时长

// 步骤3:通过 值 的更新监听器,将改变的对象手动赋值给当前对象
// 此处是将 改变后的坐标值对象 赋给 当前的坐标值对象
            // 设置 值的更新监听器
            // 即每当坐标值(Point对象)更新一次,该方法就会被调用一次
            anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    currentPoint = (Point) animation.getAnimatedValue();
                    // 将每次变化后的坐标值(估值器PointEvaluator中evaluate()返回的Piont对象值)到当前坐标值对象(currentPoint)
                    // 从而更新当前坐标值(currentPoint)

// 步骤4:每次赋值后就重新绘制,从而实现动画效果
                    invalidate();
                    // 调用invalidate()后,就会刷新View,即才能看到重新绘制的界面,即onDraw()会被重新调用一次
                    // 所以坐标值每改变一次,就会调用onDraw()一次
                }
            });
            anim.start();
            // 启动动画
        } else {
            // 如果坐标值不为0,则画圆
            // 所以坐标值每改变一次,就会调用onDraw()一次,就会画一次圆,从而实现动画效果
            // 在该点画一个圆:圆心 = (30,30),半径 = 30
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);
        }
    }
}
3.4 ObjectAnimator

直接对对象的属性值进行改变操作,从而实现动画效果

3.4.1 java code
ObjectAnimator animator = ObjectAnimator.ofFloat(Object object, String property, float ....values);  

// ofFloat()作用有两个
// 1. 创建动画实例
// 2. 参数设置:参数说明如下
// Object object:需要操作的对象
// String property:需要操作的对象的属性
// float ....values:动画初始值 & 结束值(不固定长度)
// 若是两个参数a,b,则动画效果则是从属性的a值到b值
// 若是三个参数a,b,c,则则动画效果则是从属性的a值到b值再到c值
// 以此类推
// 至于如何从初始值 过渡到 结束值,同样是由估值器决定,此处ObjectAnimator.ofFloat()是有系统内置的浮点型估值器FloatEvaluator,同ValueAnimator讲解

anim.setDuration(500);
        // 设置动画运行的时长

        anim.setStartDelay(500);
        // 设置动画延迟播放时间

        anim.setRepeatCount(0);
        // 设置动画重复播放次数 = 重放次数+1
        // 动画播放次数 = infinite时,动画无限重复

        anim.setRepeatMode(ValueAnimator.RESTART);
        // 设置重复播放动画模式
        // ValueAnimator.RESTART(默认):正序重放
        // ValueAnimator.REVERSE:倒序回放

animator.start();  
// 启动动画
3.4.2 xml
步骤1:在路径 res/animator 的文件夹里创建动画效果.xml文件
此处设置为res/animator/set_animation.xml

步骤2:设置动画参数
set_animation.xml

// ObjectAnimator 采用  标签
  
在Java代码中启动动画

Animator animator = AnimatorInflater.loadAnimator(context, R.animator.view_animation);  
// 载入XML动画

animator.setTarget(view);  
// 设置动画对象

animator.start();  
// 启动动画
3.5 TypeEvaluator

估值器,决定初值如何变化到终值
示例参考3.3.3

3.6 Interpolator
animation.setInterpolator(new AccelerateInterpolator()); 
android:interpolator="@android:anim/accelerate_interpolator" 设置动画为加速动画(动画播放中越来越快)  
 
animation.setInterpolator(new DecelerateInterpolator());  
android:interpolator="@android:anim/decelerate_interpolator" 设置动画为减速动画(动画播放中越来越慢)  
  
animation.setInterpolator(new AccelerateDecelerateInterpolator()); 
android:interpolator="@android:anim/accelerate_decelerate_interpolator" 设置动画为先加速在减速(开始速度最快 逐渐减慢)  
  
animation.setInterpolator(new AnticipateInterpolator());  
android:interpolator="@android:anim/anticipate_interpolator" 先反向执行一段,然后再加速反向回来(相当于我们弹簧,先反向压缩一小段,然后在加速弹出)  
  
animation.setInterpolator(new AnticipateOvershootInterpolator()); 
android:interpolator="@android:anim/anticipate_overshoot_interpolator" 同上先反向一段,然后加速反向回来,执行完毕自带回弹效果(更形象的弹簧效果)  
  
animation.setInterpolator(new BounceInterpolator());  
android:interpolator="@android:anim/bounce_interpolator" 执行完毕之后会回弹跳跃几段(相当于我们高空掉下一颗皮球,到地面是会跳动几下)  
  
animation.setInterpolator(new CycleInterpolator(2));
android:interpolator="@android:anim/cycle_interpolator" 循环,动画循环一定次数,值的改变为一正弦函数:Math.sin(2* mCycles* Math.PI* input)  
  
animation.setInterpolator(new LinearInterpolator());  
android:interpolator="@android:anim/linear_interpolator" 线性均匀改变  
  
animation.setInterpolator(new OvershootInterpolator());  
android:interpolator="@android:anim/overshoot_interpolator" 加速执行,结束之后回弹  
3.7 组合动画
3.7.1 java code
// 步骤1:设置需要组合的动画效果
ObjectAnimator translation = ObjectAnimator.ofFloat(mButton, "translationX", curTranslationX, 300,curTranslationX);  
// 平移动画
ObjectAnimator rotate = ObjectAnimator.ofFloat(mButton, "rotation", 0f, 360f);  
// 旋转动画
ObjectAnimator alpha = ObjectAnimator.ofFloat(mButton, "alpha", 1f, 0f, 1f);  
// 透明度动画

// 步骤2:创建组合动画的对象
AnimatorSet animSet = new AnimatorSet();  

// 步骤3:根据需求组合动画
animSet.play(translation).with(rotate).before(alpha);  
animSet.setDuration(5000);  

// 步骤4:启动动画
animSet.start();
3.7.2 xml
步骤1:在 res/animator的文件夹里创建动画.xml文件
此处为 res/animator/set_animation.xml

步骤2:设置动画效果
set_animation.xml



    // 表示Set集合内的动画按顺序进行
    // ordering的属性值:sequentially & together
    // sequentially:表示set中的动画,按照先后顺序逐步进行(a 完成之后进行 b )
    // together:表示set中的动画,在同一时间同时进行,为默认值

    
        // 下面的动画同时进行
        
        
        
        
        
    

        
            // 下面的动画按序进行
            
            
            
            
        


在Java代码中启动动画

mButton = (Button) findViewById(R.id.Button);
        // 创建动画作用对象:此处以Button为例

        AnimatorSet animator = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.set_animation);
// 创建组合动画对象  &  加载XML动画
        animator.setTarget(mButton);
        // 设置动画作用对象
        animator.start();
        // 启动动画
3.8 ViewPropertyAnimator

View自由的一种属性动画的简写方式

// 使用解析
        View.animate().xxx().xxx();
        // ViewPropertyAnimator的功能建立在animate()上
        // 调用animate()方法返回值是一个ViewPropertyAnimator对象,之后的调用的所有方法都是通过该实例完成
        // 调用该实例的各种方法来实现动画效果
        // ViewPropertyAnimator所有接口方法都使用连缀语法来设计,每个方法的返回值都是它自身的实例
        // 因此调用完一个方法后可直接连缀调用另一方法,即可通过一行代码就完成所有动画效果
        
// 以下是例子
        mButton = (Button) findViewById(R.id.Button);
        // 创建动画作用对象:此处以Button为例

        mButton.animate().alpha(0f);
        // 单个动画设置:将按钮变成透明状态 
        mButton.animate().alpha(0f).setDuration(5000).setInterpolator(new BounceInterpolator());
        // 单个动画效果设置 & 参数设置 
        mButton.animate().alpha(0f).x(500).y(500);
        // 组合动画:将按钮变成透明状态再移动到(500,500)处
        
        // 特别注意:
        // 动画自动启动,无需调用start()方法.因为新的接口中使用了隐式启动动画的功能,只要我们将动画定义完成后,动画就会自动启动
        // 该机制对于组合动画也同样有效,只要不断地连缀新的方法,那么动画就不会立刻执行,等到所有在ViewPropertyAnimator上设置的方法都执行完毕后,动画就会自动启动
        // 如果不想使用这一默认机制,也可以显式地调用start()方法来启动动画
3.9 监听
3.9.1 AnimatorListener 纯接口
Animation.addListener(new AnimatorListener() {
          @Override
          public void onAnimationStart(Animation animation) {
              //动画开始时执行
          }
      
           @Override
          public void onAnimationRepeat(Animation animation) {
              //动画重复时执行
          }

         @Override
          public void onAnimationCancel()(Animation animation) {
              //动画取消时执行
          }
    
          @Override
          public void onAnimationEnd(Animation animation) {
              //动画结束时执行
          }
      });
3.9.3 监听适配器AnimatorListenerAdapter,不用全部重写
anim.addListener(new AnimatorListenerAdapter() {  
// 向addListener()方法中传入适配器对象AnimatorListenerAdapter()
// 由于AnimatorListenerAdapter中已经实现好每个接口
// 所以这里不实现全部方法也不会报错
    @Override  
    public void onAnimationStart(Animator animation) {  
    // 如想只想监听动画开始时刻,就只需要单独重写该方法就可以
    }  
});

4 特别注意

如何手动设置对象类属性的 set() & get()
a. 背景
ObjectAnimator 类 自动赋给对象的属性 的本质是调用该对象属性的set() & get()方法进行赋值
所以,ObjectAnimator.ofFloat(Object object, String property, float ....values)的第二个参数传入值的作用是:让ObjectAnimator类根据传入的属性名 去寻找 该对象对应属性名的 set() & get()方法,从而进行对象属性值的赋值
从上面的原理可知,如果想让对象的属性a的动画生效,属性a需要同时满足下面两个条件:

对象必须要提供属性a的set()方法
a. 如果没传递初始值,那么需要提供get()方法,因为系统要去拿属性a的初始值
b. 若该条件不满足,程序直接Crash

对象提供的 属性a的set()方法 对 属性a的改变 必须通过某种方法反映出来
a. 如带来ui上的变化
b. 若这条不满足,动画无效,但不会Crash)

上述条件,一般第二条都会满足,主要是在第一条

比如说:由于View的setWidth()并不是设置View的宽度,而是设置View的最大宽度和最小宽度的;所以通过setWidth()无法改变控件的宽度;所以对View视图的width做属性动画没有效果
具体请看下面Button按钮的例子
Button mButton = (Button) findViewById(R.id.Button);
// 创建动画作用对象:此处以Button为例
// 此Button的宽高设置具体为具体宽度200px

           ObjectAnimator.ofInt(mButton, "width", 500).setDuration(5000).start();
             // 设置动画的对象,但这个动画不会有任何效果

5 核心源码及注释

http://blog.csdn.net/u013378580/article/details/51917884

鸣谢Carson_Ho

你可能感兴趣的:(android动画)