Android-逐帧动画、补间卡通学习

Android--逐帧动画、补间动画学习

I、逐帧动画

逐帧动画用以指定一系列用作View的背景的Drawable对象,感觉类似于幻灯片。

public class AnimationDrawable extends DrawableContainer implements Runnable, Animatable

public class DrawableContainer extends Drawable implements Drawable.Callback

一、应用实例:
1、在res/drawabe文件下建立<animation-list>
Android-逐帧动画、补间卡通学习
<?xml version="1.0"encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
   android:oneshot="false">
    <item android:drawable="@drawable/icon0" android:duration="500"/>
    <item android:drawable="@drawable/icon1" android:duration="500"/>
    <item android:drawable="@drawable/icon2" android:duration="500"/>
    <item android:drawable="@drawable/icon3" android:duration="500"/>
</animation-list>

 android:oneshot为false时表示循环播放,默认为false

2、代码中使用逐帧动画
myImageView=(ImageView)findViewById(R.id.myImageView);
myImageView.setBackgroundResource(R.drawable.myanimation);
       
AnimationDrawable animationDrawable =(AnimationDrawable) myImageView.getBackground();
animationDrawable.start();
//animationDrawable.stop();

二、扩展知识:AnimationDrawable类

1、提供函数:

public void start()
public void stop()
public boolean isRunning()
public int getNumberOfFrames()
public Drawable getFrame(int index)
public int getDuration(int i)
public boolean isOneShot()
public void setOneShot(boolean oneShot)
/**Add a frame to the animation */
public void addFrame(Drawable frame, int duration)

2、已定义了一个帧动画,如何做到播放其中的某几个连续帧;(比如本文中有4个帧,播放2-3帧)

分析:AnimationDrawable类提供了getFrame和getDuration方法,但是未提供相应的setter函数来控制播放的帧位置。AnimationDrawable类中有一个私有变量mCurFrame表示当前的播放帧。定义如下:

/** The current frame, may be -1 when not animating. */
    private int mCurFrame = -1;
因为其为private变量,且未提供相应setter函数,故采用反射技术进行读写。
public class MyImageView extends ImageView{
   
    private AnimationDrawable animationDrawable;
 
    public MyImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setBackgroundResource(R.drawable.myanimation);
        animationDrawable = (AnimationDrawable) getBackground();
       
        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                animationDrawable.start();
            }
        });
    }
   
    @Override
    public void onDraw(Canvas canvas)
    {
        try
        {
           Field field = AnimationDrawable.class.getDeclaredField("mCurFrame");
           field.setAccessible(true);
           int curFrame = field.getInt(animationDrawable);
           if(curFrame == 2)
              field.setInt(animationDrawable, 0);
        } catch(Exception e)
        {
           Log.e("AnimationDrawable", e.toString());
        }
        super.onDraw(canvas);
    }
 
}

II、补间动画:
不同于逐帧动画,即设定好动画起始终止状态,中间由系统来补充
一、四种基本动画
Android-逐帧动画、补间卡通学习
1、缩放scale---ScaleAnimation
方法一:在res/anim文件下创建xml文件:
 <!-- 缩放 -->
     <scale
         android:fromXScale="0.0"  android:toXScale="1.0"
         android:fromYScale="0.0"  android:toYScale="1.0"
         android:pivotX="50%"
         android:pivotY="50%"
         android:duration="1000"
         android:repeatCount="-1"
         android:repeatMode="reverse"
         />
         <!-- fromXScale:沿X,Y轴的缩放比例  from-to-->
         <!-- pivotX:缩放的中心点位置,(占View大小的百分比) -->
         <!-- duration:持续时间 -->
         <!-- repeatCount:重复次数,默认为0,代表旋转一次;则设为n,旋转n+1次;设为-1或infinite,则为无限循环 -->
         <!-- repeatMode:重复模式,默认为restart-->
代码中使用:
    Animation animation =AnimationUtils.loadAnimation(MainActivity.this,R.anim.anim_set);
    myImageView.startAnimation(animation);

方法二: 
    ScaleAnimation animation = new ScaleAnimation(0, 1, 0, 1);
    animation.setDuration(1000);
    animation.setRepeatCount(-1);
    animation.setRepeatMode(Animation.RESTART);
    myImageView.startAnimation(animation);

下面用法大同小异:

2、透明度alpha --- AlphaAnimation

    <!-- 透明度 -->
    <alpha
        android:fromAlpha="0.0"  android:toAlpha="1.0"
        android:duration="1000"
        android:repeatCount="-1"
        android:repeatMode="reverse"
        />
3、旋转rotate --- RotateAnimation
    <!-- 旋转 -->
    <rotate
        android:fromDegrees="0"  android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="1000"
        android:repeatCount="2"
        android:repeatMode="restart"
        />
4、移动translate --- TranslateAnimation
    <!-- 移动 -->
    <translate
        android:fromXDelta="0"   android:toXDelta="1000"
        android:fromYDelta="0"   android:toYDelta="1000"
        android:duration="1000"
        />

二、几种Anim的组合set
<?xmlversion="1.0"encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:anim/accelerate_interpolator"
     android:repeatMode="reverse"
     android:repeatCount="2"
     >
    <!-- 移动 -->
    <translate
        android:fromXDelta="0"   android:toXDelta="1000"
        android:fromYDelta="0"   android:toYDelta="1000"
        android:duration="1000"
        />
   
    <!-- 缩放 -->
     <scale
         android:fromXScale="0.0"  android:toXScale="1.0"
         android:fromYScale="0.0"  android:toYScale="1.0"
         android:pivotX="50%"
         android:pivotY="50%"
         android:duration="1000"
     />
    
   <!-- 透明度 -->
    <alpha
        android:fromAlpha="0.0"  android:toAlpha="1.0"
        android:duration="1000"
        />
   
     <rotate
        android:fromDegrees="0"  android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="1000"
        />
</set>
android:interpolator是指每个动画开始和结束值间转换中所使用的差值器(动画渲染器),默认为AccelerateDecelerateInterpolator
也可通过setInterpolator设置;
Android-逐帧动画、补间卡通学习
AccelerateDecelerateInterpolator:  开始和结束时变化较慢,中间时候加速

An interpolator where the rate of change starts and ends slowly but accelerates through the middle

AccelerateInterpolator                :  开始较慢,然后逐渐加速

An interpolator where the rate of change starts out slowly and and then accelerates.

AnticipateInterpolator               : 开始时向后,然后再向前急冲

An interpolator where the change starts backward then flings forward.

AnticipateOvershootInterpolator:开始时向后,然后再向前急冲一定值之后,最后回到最终值

An interpolator where the change starts backward then flings forward and overshoots the target value and finally goes back to the final value.

BounceInterpolator                   : 动画结束时弹起

An interpolator where the change bounces at the end.

DecelerateInterpolator               ; 开始时速度较快,然后减速

An interpolator where the rate of change starts out quickly and and then decelerates.

LinearInterpolator                      ; 变化速度为常量

An interpolator where the rate of change is constant

OvershootInterpolator               ; 开始向前急冲,超过最终值,然后返回

An interpolator where the change flings forward and overshoots the last value then comes back.


三、添加动画监听器 AnimationListener

animation.setAnimationListener(new AnimationListener() {
                   
        @Override
        public void onAnimationStart(Animation animation) {
            Log.i("AnimationListener", "动画开始");
        }
       
        @Override
        public void onAnimationRepeat(Animation animation) {
            Log.i("AnimationListener", "动画重复");
        }
       
        @Override
        public void onAnimationEnd(Animation animation) {
            Log.i("AnimationListener", "动画结束");
        }
    });
四、为ViewGroup添加动画
    布局动画LayoutAnimation:
1、在res/anim下创建xml文件:layout_anim.xml
<?xmlversion="1.0"encoding="utf-8"?>
<layoutAnimation
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:delay="1.0"
    android:animationOrder="random"
    android:animation="@anim/anim_set"
    />
 
<!-- delay 子类动画时间间隔(延迟) -->
<!-- animationOrder 子类的显示方式: normal(0 默认) reverse(1 倒序) random(2 随机)  -->
<!-- animation : 对应动画 -->
2、使用:在activity的layout界面布局中写入:
    android:layoutAnimation="@anim/layout_anim"
3、在Java代码中使用:

<为ListView(ViewGroup的子类)添加动画>

    String[] mMenuItemStr = { "Bear", "Bird", "Cat", "Tigers", "Panda" ,"Bear", "Bird", "Cat", "Tigers", "Panda"};
    listView.setAdapter(new ArrayAdapter<String>(this, R.layout.item_left_menu,mMenuItemStr));
 
    Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_set);
    LayoutAnimationController controller = new LayoutAnimationController(animation);
    controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
    controller.setDelay(1);
    listView.setLayoutAnimation(controller);
4、实现效果是为ViewGroup中每一个子View添加了动画,并按照Random等方式进行加载

5、ViewGroup第一次进行布局时加载布局动画。可以通过listView.scheduleLayoutAnimation();强制其再次执行

6、添加动画监听:

     listView.setLayoutAnimationListener(new AnimationListener() {           
            @Override
            publicvoid onAnimationStart(Animation animation) {
               
            }           
            @Override
            publicvoid onAnimationRepeat(Animation animation) {
               
            }           
            @Override
            publicvoid onAnimationEnd(Animation animation) {
               
            }
        });

五、拓展:
1、现有两个补间动画(anim1.xml,anim2.xml),如何做到两个补间动画在一个播放完后就立刻播放另一个动画。
使用动画监听,重写onAnimationEnd方法即可
private Animation animation1, animation2;
   
    privateclass myAnimationListener implements AnimationListener
    {
        @Override
        publicvoid onAnimationStart(Animation animation) {
           
        }
        @Override
        publicvoid onAnimationEnd(Animation animation) {
            if(animation.hashCode() == animation1.hashCode())
                myImageView.startAnimation(animation2);
            else
                myImageView.startAnimation(animation1);
        }
        @Override
        publicvoid onAnimationRepeat(Animation animation) {
           
        }      
    }
在Activity中使用得:
        animation1 = AnimationUtils.loadAnimation(MainActivity.this, R.anim.anim1);
        animation2 = AnimationUtils.loadAnimation(MainActivity.this, R.anim.anim2);
        myAnimationListener myAnimationListener = new myAnimationListener();
        animation1.setAnimationListener(myAnimationListener);
        animation2.setAnimationListener(myAnimationListener);
        myImageView.startAnimation(animation1);
2、自定义动画渲染器interpolator
    private class myInterpolator implements Interpolator
    {
        @Override
        public float getInterpolation(floatinput) {
           
            /**input: 取值范围 0.0-1.0,表示动画的加载进度
             * 返回值:<1.0表示还未达到目标点;越接近1.0表示离目标点越近;大于1.0表示动画对象已经超过了目标点**/
            if(input <= 0.5)
                return input * input;
            else
                return (1 - input) * (1 - input);
        }    
    }
在代码中使用:
    Animation animation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.anim1);
    animation.setInterpolator(new myInterpolator());
    myImageView.startAnimation(animation);
对比看一下AccelerateInterpolator的源码:
/**
 * An interpolator where the rate of change starts out slowly and
 * and then accelerates.
 *
 */
@HasNativeInterpolator
public class AccelerateInterpolator implements Interpolator, NativeInterpolatorFactory {
    private final floatmFactor;
    private final doublemDoubleFactor;
 
    public AccelerateInterpolator() {
        mFactor = 1.0f;
        mDoubleFactor = 2.0;
    }
 
    /**
     * Constructor
     *
     * @param factor Degree to which the animation should be eased. Seting
     *        factor to 1.0f produces a y=x^2 parabola. Increasing factor above
     *        1.0f  exaggerates the ease-in effect (i.e., it starts even
     *        slower and ends evens faster)
     */
    public AccelerateInterpolator(floatfactor) {
        mFactor = factor;
        mDoubleFactor = 2 * mFactor;
    }
 
    public AccelerateInterpolator(Context context, AttributeSet attrs) {
        this(context.getResources(), context.getTheme(), attrs);
    }
 
    /** @hide */
    public AccelerateInterpolator(Resources res, Theme theme, AttributeSet attrs) {
        TypedArray a;
        if (theme != null) {
            a = theme.obtainStyledAttributes(attrs, R.styleable.AccelerateInterpolator, 0, 0);
        } else {
            a = res.obtainAttributes(attrs, R.styleable.AccelerateInterpolator);
        }
 
        mFactor = a.getFloat(R.styleable.AccelerateInterpolator_factor, 1.0f);
        mDoubleFactor = 2 * mFactor;
 
        a.recycle();
    }
 
    public float getInterpolation(float input) {
        if (mFactor == 1.0f) {
            return input * input;
        } else {
            return (float)Math.pow(input, mDoubleFactor);
        }
    }
 
    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createAccelerateInterpolator(mFactor);
    }
}