Android动画

Android View动画

先来看animation继承关系


Android动画_第1张图片
图片标题

  • AlphaAnimation 透明动画
  • AnimationSet 动画集合
  • RotateAnimation 旋转动画
  • ScaleAnimation 缩放动画
  • TranslateAnimation 位移动画

这几个动画是android3.0之前的,被称为视图动画,3.0后出现了属性动画,视图动画有一个
很大的弊端,当对某个View操作后,无法改变响应事件位置,意思就是说发生动画后,其响应事件
的位置还在动画之前。根据android文档对animation机制的叙述

Android provides two mechanisms that you can use to create simpleanimations: tweened animation, in 
which you tell Android to perform a series of simple transformations 
(position, size, rotation, and so on) to the content of a View; and frame-by-frame animation, which loads a 
series of Drawable resources one after the other. Both animation types can be used in any View object to 
provide simple rotating timers, activity icons, and other useful UI elements.
Tweened animation is handled by this package (android.view.animation);
frame-by-frame animation is handled by the AnimationDrawable class.
大意为android提供了两种动画机制,分别是tweened animation和frame-by-frame animation
补间动画 (tweened animation):
对View进行一系列操作,淡出,缩放,评议,旋转
实现补间动画需要animation类,也就是前面所说的视图动画,可以在xml里定义,也可以在java代码
里定义
逐帧动画 (frame-by-frame animation):
将一个完整的动画拆分成一张张单独的图片,再把它连贯起来播放
实现逐帧动画,在java代码里需要AnimationDrawable类,也可以在xml文件实现
  • Android群英传:Animation框架定义了透明度,旋转,缩放,旋转,位移几种常见动画,控制
    的是整个View,实现原理是每次绘制试图时View所在的ViewGroup中的drawChild方法获取
    该View的Animation的Transformation值,然后调用canvas.concat(transfromToApply.getMatrix())
    方法,通过矩阵运算完成动画帧,如果动画没有完成就继续调用invaildate方法,启动下次绘制来
    驱动动画

1.逐帧动画 (frame-by-frame animation):

1.1) 在xml书写

在 res/drawable/目录,新建xml文件


    
    
    

oneshot:true则动画只播放一次,如果为false则循环播放动画,如果不设置则默认为fasle.
在java代码里

AnimationDrawable rocketAnimation;//需要AnimationDrawable对象实现,

public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
  rocketImage.setBackgroundResource(R.drawable.rocket_thrust);
  rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
}

public boolean onTouchEvent(MotionEvent event) {
  if (event.getAction() == MotionEvent.ACTION_DOWN) {
    rocketAnimation.start();//开启动画
    return true;
  }
  return super.onTouchEvent(event);
}

文档里说明了不要在onCreate调用rocketAnimation.start()

It's important to note that the start() method called on the AnimationDrawable cannot be called during the 
onCreate() method of your Activity, because the AnimationDrawable is not yet fully attached to the window. If 
you want to play the animation immediately, without requiring interaction, then you might want to call it from 
the onWindowFocusChanged() method in your Activity, which will get called when Android brings your 
window into focus.

1.2) 完全在java代码里定义

    Button btnPlay;  
    ImageView ivImage;  
    Button btnStop;  
    private AnimationDrawable anim;  
       
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout. activity_main);  
        ivImage=(ImageView) findViewById(R.id. iv_image);  
         
        anim = new AnimationDrawable();   
        for (int i = 1; i <=3; i++) {  
            //根据资源名称和目录获取R.java中对应的资源ID   
            int id = getResources().getIdentifier("rocket_thrust" + i, "drawable" , getPackageName());   
            //根据资源ID获取到 Drawable对象   
            Drawable drawable = getResources().getDrawable(id);   
            //将此帧添加到AnimationDrawable中   
            anim.addFrame(drawable,100);   
        }   
        //设置循环播放  
        anim.setOneShot( false);   
        //设置图片的背景为我们的动画  
        ivImage .setBackgroundDrawable (anim);  
    }  
     
    @Override  
    public void onWindowFocusChanged( boolean hasFocus) {  
      super.onWindowFocusChanged(hasFocus);  
      //启动动画  
      anim.start();  
    }     

2.补间动画 (tweened animation):

android的补间动画需android.view.animation下的子类实现:

  • AlphaAnimation:透明度(alpha)动画,对应< alpha/>
  
  
  • TranslateAnimation:位移动画,对应< translate/>
   
      
 
  • ScaleAnimation:缩放动画,对应< scale/>
  
  
  
  • RotateAnimation:旋转动画,对应< rotate/>
  
 
  • AnimationSet:动画集合,对应< set/>
  
      
      
      
      

在java代码里为某个view启动动画

Animation animation = AnimationUtils.loadAnimation(this,R.anim.demo);  
imageView.startAnimation(animation); 

上面的是在xml文件里实现,下面在java代码完全实现

  • 透明度动画
AlphaAnimation animation = new AlphaAnimation(0.5f, 1);
animation.setDuration(4000);
view.startAnimation(animation);
  • 位移动画
TranslateAnimation translateAnimation = new TranslateAnimation(0, 200, 0, 400);//起始x,y坐标
translateAnimation.setRepeatCount(2);
translateAnimation.setDuration(2000);
view.startAnimation(translateAnimation);
  • 缩放动画
ScaleAnimation scaleAnimation = new ScaleAnimation(0,2,0,2,
Animation.RELATIVE_TO_SELF,0.5f,
Animation.RELATIVE_TO_SELF,0.5f);//后4个参数指定自身为旋转点
scaleAnimation.setDuration(2000);
view.startAnimation(scaleAnimation);
  • 旋转动画
// new RotateAnimation(0, 360, 100, 100);//起始角度以及旋转坐标
RotateAnimation rotateAnimation = new RotateAnimation(0, 360, 
        RotateAnimation.RELATIVE_TO_SELF, 0.5f, 
        RotateAnimation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(3000);
rotateAnimation.setInterpolator(new OvershootInterpolator());
view.startAnimation(rotateAnimation);
  • 动画集合
AnimationSet animationSet = new AnimationSet(true);//共享插值器
TranslateAnimation translateAnimation = new TranslateAnimation(0, 200, 0, 400);//起始x,y坐标
translateAnimation.setRepeatCount(2);
translateAnimation.setDuration(2000);
animationSet.addAnimation(translateAnimation);

RotateAnimation rotateAnimation = new RotateAnimation(0, 360, 
        RotateAnimation.RELATIVE_TO_SELF, 0.5f, 
        RotateAnimation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(3000);
rotateAnimation.setInterpolator(new OvershootInterpolator());
animationSet.addAnimation(rotateAnimation);

view.startAnimation(animationSet);
  • Animation可以设置插值器,一些种类如下
LinearInterpolator(匀速)
AccelerateInterpolator(先慢后快)
AccelerateDecelerateInterpolator(先慢中快后慢)
DecelerateInterpolator(先快后慢)
CycleInterpolator(循环播放,速度为正弦曲线)
AnticipateInterpolator(先回撤,再匀速向前)
OvershootInterpolator(超过,拉回)
BounceInterpolator(回弹)

还有几个常用的方法

Animation.setFillAfter(true);//动画之后停留在动画后的界面
Animation.setAnimationListener(Animation.AnimationListener listener);//设置动画监听

关于补间动画和逐帧动画的代码演示到这里,前面说过,这两个动画有弊端,谷歌定义了属性动画,来
解决补间动画和逐帧动画不足的问题

3.属性动画(Property Animation)

下面来看看Animator类继承关系,此类的子类是实现属性动画的关键


Android动画_第2张图片
图片标题
  • ValueAnimator
    ValueAnimator是一个可以数值不断规律变化的类
    郭霖:*属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束
    值之间的动画过渡就是由ValueAnimator这个类来负责计算的 *,ValueAnimator的简
    单用法,设置起始值分别为0,100,调用start方法就可以实现0"滑"到100,并设置
    数值监听,以及开始和结束监听
public void onClick(View v){
        final ValueAnimator animator = ValueAnimator.ofFloat(0,100);
        animator.setDuration(1000);
        animator.addListener(new AnimatorListener() {
            
            @Override
            public void onAnimationStart(Animator animation) {
                Log.e("Tag", "valueanimator开始");
            }
            @Override
            public void onAnimationRepeat(Animator animation) {
            }
            @Override
            public void onAnimationEnd(Animator animation) {
                Log.e("Tag", "valueanimator结束");
            }
            @Override
            public void onAnimationCancel(Animator animation) {
            }
        });
        
        animator.addUpdateListener(new AnimatorUpdateListener() {
            
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // TODO Auto-generated method stub
                float currentValue = (Float) animator.getAnimatedValue();
                Log.e("MainActivity", currentValue+"");
            }
        });
        
        animator.start();
}
////////////////////////输出如下/////////////////////////////
09-05 02:39:37.686: E/MainActivity(922): 0.0
09-05 02:39:37.686: E/Tag(922): valueanimator开始
09-05 02:39:37.706: E/MainActivity(922): 0.0
09-05 02:39:37.777: E/MainActivity(922): 1.3091236
09-05 02:39:37.836: E/MainActivity(922): 3.9271564
09-05 02:39:37.916: E/MainActivity(922): 10.588717
09-05 02:39:37.947: E/MainActivity(922): 13.337236
09-05 02:39:38.018: E/MainActivity(922): 22.025887
09-05 02:39:38.114: E/MainActivity(922): 34.998005
09-05 02:39:38.126: E/MainActivity(922): 37.87003
09-05 02:39:38.226: E/MainActivity(922): 51.09946
09-05 02:39:38.306: E/MainActivity(922): 65.30138
09-05 02:39:38.396: E/MainActivity(922): 75.72197
09-05 02:39:38.487: E/MainActivity(922): 86.44843
09-05 02:39:38.562: E/MainActivity(922): 94.69208
09-05 02:39:38.586: E/MainActivity(922): 96.19398
09-05 02:39:38.660: E/MainActivity(922): 99.3343
09-05 02:39:38.730: E/MainActivity(922): 100.0
09-05 02:39:38.730: E/Tag(922): valueanimator结束


还可以调用setStartDelay()方法来设置动画延迟播放的时间,
调用setRepeatCount()和setRepeatMode()方法来设置动画循环播放的次数以及循环
播放的模式,循环模式包括RESTART和REVERSE两种,分别表示重新播放和倒序播放的

ValueAnimator只是一个单纯的数值发生器,并没有直接操作View来产生动画的效果
那么如何利用ValueAnimator来实现View动画效果呢?

基本解决思路是写一个自定义View,在自定义View里定义一个数值,让ValueAnimator不断
修改这个数值,再调用自定义View的invalidate()方法引起重绘

public class CustomView extends View {
    private Paint mPaint;//画笔
    private int mRadius = 50;//初始大小
    private boolean FLAG = true;//是否要重新计算数值
    
    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    public CustomView(Context context) {
        super(context);
        init();
    }
    private void init() {
        mPaint = new Paint();
        mPaint.setColor(Color.BLUE);
        mPaint.setAntiAlias(true);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        drawCircle(canvas);
        if(FLAG) startAnim();
    }

    private void drawCircle(Canvas canvas) {
        canvas.drawCircle(200, 200, mRadius, mPaint);
    }

    private void startAnim() {
        ValueAnimator animator = ValueAnimator.ofInt(0,200,0);//从0开始变化到200,再200变化到0
        animator.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (Integer) animation.getAnimatedValue();
                mRadius = value;
                invalidate();
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                FLAG = false;//当开始了数值变化,设置false,那样在重新调用onDraw就不会走startAnim()
            }
            @Override
            public void onAnimationEnd(Animator animation) {
                FLAG = true;
            }
        });
        animator.setDuration(5000);
        animator.start();
    }
}

这里在设置listener时,传入了一个AnimatorListenerAdapter,用这个类只需覆盖自己想实现的方法就可以,效果就是一个蓝色的圆不停的缩放,就不放图了
当然,ValueAnimator不但可以对数值进行操作,还可以操作对象,那就是用ValueAnimator的ofObject方法
ofObject(TypeEvaluator evaluator, Object... values)
第一个参数接受的是TypeEvaluator类型对象,第二个要求传Object可变参数,TypeEvaluator是关键,决定了值怎么变化,如果要自己实现一个基于颜色变化的值,就需要实现它,当然android提供了一个ArgbEvaluator可以实现颜色变化,如果我要将前面代码修改,把mRadius封装成对象,变成

public class Radius {
    private int radius;
    public Radius(int radius) {
        this.radius = radius;
    }
    public int getRadius() {
        return radius;
    }
}

非常简单的封装,然后再创建一个自己的TypeEvaluator

public class RadiusEvaluator implements TypeEvaluator {

    @Override
    public Radius evaluate(float fraction, Radius startValue, Radius endValue) {
        int startInt = startValue.getRadius();
        int newRadius = (int)(startInt + fraction * (endValue.getRadius() - startInt));
        return new Radius(newRadius);
    }
}

看上去代码也就几行,先得到传入对象的半径,
赋值给startInt(开始值),开始值 + fraction * (结束值- 初始值),fraction是动画完成度,介于[0,1]之间,不断
向1变大大,再把之前的代码改一改

public class CustomView extends View {

    private Paint mPaint;
    private Radius mRadius;
    private boolean FLAG = true;
    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    public CustomView(Context context) {
        super(context);
        init();
    }

    private void init() {
        mPaint = new Paint();
        mPaint.setColor(Color.BLUE);
        mPaint.setAntiAlias(true);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        if(mRadius == null) {
            mRadius = new Radius(30);
        }
        drawCircle(canvas);
        if(FLAG) startAnim();
    }

    private void drawCircle(Canvas canvas) {
        canvas.drawCircle(200, 200, mRadius.getRadius(), mPaint);
    }

    private void startAnim() {
        Radius startValue = new Radius(0);
        Radius endValue = new Radius(200);
        ValueAnimator animator = ValueAnimator.ofObject(new RadiusEvaluator(),startValue ,endValue);
        animator.addUpdateListener(new AnimatorUpdateListener() {
            
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Radius value = (Radius) animation.getAnimatedValue();
                mRadius = value;
                invalidate();
            }
        });
        
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                FLAG = false;
            }
            @Override
            public void onAnimationEnd(Animator animation) {
                FLAG = true;
            }
        });
        animator.setDuration(5000);
        animator.start();
    }
    
}

ValueAnimator的简单用法就是以上

  • ObjectAnimator

对于ValueAnimator,它是对数值进行操作,而ObjectAnimator,它继承自ValueAnimator,可以直接操作View产生动画效果

public void onClick(View v){
    ObjectAnimator.ofFloat(v, "translationX", 0,300).setDuration(3000).start();
}

当点击Button时,会调用onClick方法,ObjectAnimator.ofFloat的第一个参数是要操作哪个对象,第二个参数是要传入
操作对象的属性,这个属性必须要有set get方法,内部是通过反射来调用set方法.
,第三个参数是要从哪个值开始变化,第四个参数是要平滑至哪个数值,这里填了0,300,表示x坐标从0变化到300

  • ObjectAnimator+AnimatorSet实现组合动画
/*
实现组合动画功能主要需要借助AnimatorSet这个类,这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:
after(Animator anim)   将现有动画插入到传入的动画之后执行
after(long delay)   将现有动画延迟指定毫秒后执行
before(Animator anim)   将现有动画插入到传入的动画之前执行
with(Animator anim)   将现有动画和传入的动画同时执行
*/
ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f);  
ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);  
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);  
AnimatorSet animSet = new AnimatorSet();  
animSet.play(rotate).with(fadeInOut).after(moveIn);  
animSet.setDuration(5000);  
animSet.start(); 
/*
可以看到,这里我们先是把三个动画的对象全部创建出来,然后new出一个AnimatorSet对象之后将这三个动画对象进行播
放排序,让旋转和淡入淡出动画同时进行,并把它们插入到了平移动画的后面,最后是设置动画时长以及启动动画
*/

现在重新修改CustomView,让在绘制圆的时候实现颜色渐变

public class CustomView extends View {
    private Paint mPaint;
    private Radius mRadius;
    private boolean FLAG = true;
    private int color;  
      
    public int getColor() {  
         return color;  
    }  
    
    public void setColor(int color) {  
         this.color = color;  
         mPaint.setColor(color);  
         invalidate();  
    } 
    
    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    
    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    
    public CustomView(Context context) {
        super(context);
        init();
    }
    
    private void init() {
        mPaint = new Paint();
        mPaint.setColor(Color.BLUE);
        mPaint.setAntiAlias(true);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        if(mRadius == null) {
            mRadius = new Radius(30);
        }
        drawCircle(canvas);
        if(FLAG) startAnim();
    }

    private void drawCircle(Canvas canvas) {
        canvas.drawCircle(200, 200, mRadius.getRadius(), mPaint);
    }

    private void startAnim() {
        Radius startValue = new Radius(0);
        Radius endValue = new Radius(200);
        ValueAnimator animator = ValueAnimator.ofObject(new RadiusEvaluator(),startValue ,endValue);
        animator.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Radius value = (Radius) animation.getAnimatedValue();
                mRadius = value;
                invalidate();
            }
        });
        
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                FLAG = false;
            }
            @Override
            public void onAnimationEnd(Animator animation) {
                FLAG = true;
            }
        });
        
        ObjectAnimator animator2 = ObjectAnimator.ofObject(this, "color", new ArgbEvaluator(), Color.BLUE,Color.GREEN);
        AnimatorSet set = new AnimatorSet();
        set.play(animator).with(animator2);
        set.setDuration(5000);
        set.start();
    }
    
}

前面说过ObjectAnimator会反射调用set方法,那么我们可以在CustomView里自己定义一个color的get,set方法,通过设置画笔颜色来改变圆的颜色,注意的是,在set方法里,需要调用invalidate()来引起View的重绘,我在后面使用ArgbEvaluator,这是android自带的api,可以实现颜色渐变,with和play表示同时执行动画效果

  • Interpolator 插值器

简单的理解,插值器是用来控制动画速度快慢的,比如,有一个动画要模拟小球从高空坠落,那么速度会
越来越快,不过要怎么实现呢,就要用到插值器了,前面补间动画就用到了插值器
而属性动画也有插值器,TimeInterpolator,下面实现一个自定义的插值器

class MyInterpolator implements TimeInterpolator{

        @Override
        public float getInterpolation(float input) {
            input += 0.1f;
            return input;
        }
    }

只需实现TimeInterpolator,重写getInterpolation()就可以自定义插值器,该方法中接收一个input参数,经过getInterpolation(float input)方法后,返回一个数值,TypeEvaluator的参数fraction就是方法返回值,上面这个input计算很简单,不断加0.1,实现匀速效果,如果直接返回一个固定值0.5,那么动画会停留在一半,因为这个返回的值是传给TypeEvaluator的evaluate方法fraction参数,传进来
始终都是一个固定值0.5,所以动画不会有渐变效果


  • 插值器和TypeEvaluator之间关系:

插值器:在动画的播放过程中Android中提供插值器来改变动画的播放速率,有些动画的速度是不变的,而有些是会变得例如模仿加下落的效果,或者类似反弹的效果
TypeEvaluator:确定ValueAnimator.getAnimatedValue()返回的具体对象类型

  • ViewPropertyAnimator
    3.1之后可以直接调用View的animate()方法来得到ViewPropertyAnimator,支持链式操作,无需调用start方法
view.animate().alpha(0f).setDuration(5000).translationX(300);

设置view从当前透明度到完全透明同时x坐标变化到300

关于android动画的笔记写到这里,下一学习内容Android系统启动流程.

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