Animation动画效果的实现

提供了三种动画效果:逐帧动画(frame-by-frame animation),这种动画和GIF一样,一帧一帧的显示来组成动画效果;布局动画(layout animation),这种动画用来设置layout内的所有UI控件;控件动画(view animation),这种是应用到具体某个view上的动画。

 
在这三种动画实现中逐帧动画是最简单的,而控件动画是有点复杂的,要涉及到线性代数中的矩阵运算,下面就由易到难逐个介绍,先来看看逐帧动画如何实现。
 
逐帧动画
逐帧动画是通过OPhone中的android.graphics.drawable.AnimationDrawable类来实现的,在该类中保存了帧序列以及显示的时间,为了简化动画的创建OPhone提供了一种通过XML来创建逐帧动画的方式,这样把动画的创建和代码分来以后如果需要修改动画内容,只需要修改资源文件就可以了不用修改代码,简化开发维护工作。在res/drawable/文件夹下创建一个XML文件,下面是一个示例文件(res\drawable\qq_animation.xml):
<animation-list    

    xmlns:android="http://schemas.android.com/apk/res/android"  


    android:oneshot="false">   


<item android:drawable="@drawable/qq001" android:duration="80"/>   


<item android:drawable="@drawable/qq002" android:duration="80"/>   


<item android:drawable="@drawable/qq003" android:duration="80"/>   


<item android:drawable="@drawable/qq004" android:duration="80"/>   


<item android:drawable="@drawable/qq005" android:duration="80"/>   


<item android:drawable="@drawable/qq006" android:duration="80"/>   


<item android:drawable="@drawable/qq007" android:duration="80"/>   


<item android:drawable="@drawable/qq008" android:duration="80"/>   

</animation-list>  
<?xml version="1.0" encoding="utf-8"?>   
<LinearLayout    

xmlns:android="http://schemas.android.com/apk/res/android"  


    android:orientation="vertical"    


    android:layout_width="fill_parent"  


    android:layout_height="fill_parent">   

    <ImageView    

       android:id="@+id/animation_view"  


       android:layout_width="fill_parent"    


       android:layout_height="wrap_content"  


       android:src="@drawable/qq_animation" />   

    <Button    

       android:id="@+id/animation_btn"    


       android:layout_width="fill_parent"  


       android:layout_height="wrap_content"    


       android:text="@string/start_animation" />   

    <Button    

       android:id="@+id/one_shot_btn"    


       android:layout_width="fill_parent"  


       android:layout_height="wrap_content"    


       android:text="@string/play_once" />   

</LinearLayout>  
public class AnimActivity extends Activity {   

    /** Called when the activity is first created. */  

    AnimationDrawable mAd;   
    Button mPlayBtn;   
    Button mOneShotBtn;   

    boolean mIsOneShot;   

    

    @Override  


    public void onCreate(Bundle savedInstanceState) {   


       super.onCreate(savedInstanceState);   

       setContentView(R.layout.main);   
    
       ImageView iv = (ImageView) findViewById(R.id.animation_view);   
       mAd = (AnimationDrawable) iv.getDrawable();   
    
       mPlayBtn = (Button) findViewById(R.id.animation_btn);   

       mPlayBtn.setOnClickListener(new OnClickListener() {   


           @Override  


           public void onClick(View view) {   

              startAnimation();   
           }   
       });   
    
       mOneShotBtn = (Button) findViewById(R.id.one_shot_btn);   

       mOneShotBtn.setOnClickListener(new OnClickListener() {   


           @Override  


           public void onClick(View view) {   


              if (mIsOneShot) {   


                  mOneShotBtn.setText("Play Once");   


              } else {   


                  mOneShotBtn.setText("Play Repeatly");   

              }   
              mAd.setOneShot(!mIsOneShot);   
              mIsOneShot = !mIsOneShot;   
           }   
       });   
    
    }   
    

    /**  
     * 通过AnimationDrawable的start函数播放动画,  
     * stop函数停止动画播放,  
     * isRunning来判断动画是否正在播放。  
     */  

    public void startAnimation() {   


       if (mAd.isRunning()) {   

           mAd.stop();   

       } else {   

           mAd.stop();   
           mAd.start();   
       }   
    }   
}   
   
<set xmlns:android="http://schemas.android.com/apk/res/android"  

android:interpolator="@android:anim/decelerate_interpolator">   

<alpha    

    android:fromAlpha="0.0"    


    android:toAlpha="1.0"    


    android:duration="1000" />   

</set>  
<alpha    

xmlns:android="http://schemas.android.com/apk/res/android"  


android:interpolator="@android:anim/accelerate_interpolator"  


android:fromAlpha="0.0"    


android:toAlpha="1.0"    


android:duration="1000" />  
<scale    

xmlns:android="http://schemas.android.com/apk/res/android"  


android:interpolator="@android:anim/accelerate_interpolator"  


android:fromXScale="1"  


android:toXScale="1"  


android:fromYScale="0.1"  


android:toYScale="1.0"  


android:duration="500"  


android:pivotX="50%"  


android:pivotY="50%"  


android:startOffset="100" />  
<rotate    

xmlns:android="http://schemas.android.com/apk/res/android"  


android:interpolator="@android:anim/accelerate_interpolator"  


android:fromDegrees="0.0"  


android:toDegrees="360"  


android:pivotX="50%"  


android:pivotY="50%"  


android:duration="500" />  
<translate    

xmlns:android="http://schemas.android.com/apk/res/android"  


android:interpolator="@android:anim/accelerate_interpolator"  


android:fromYDelta="-100%"    


android:toYDelta="0"  


android:duration="500" />  
<set xmlns:android="http://schemas.android.com/apk/res/android"  

android:interpolator="@android:anim/decelerate_interpolator">   

<translate    

android:fromYDelta="-100%"    


android:toYDelta="0"  


android:duration="500" />   

<alpha    

android:fromAlpha="0.0"    


android:toAlpha="1.0"  


android:duration="500" />   

</set>  
LayoutAnimationController,该文件同样位于res/anim/目录下,下面是一个示例(res\anim\layout_anim_ctrl.xml):   
<layoutAnimation    

xmlns:android="http://schemas.android.com/apk/res/android"  


android:delay="30%"  


android:animationOrder="reverse"  


android:animation="@anim/translate_alpha_anim" />  
<?xml version="1.0" encoding="utf-8"?>   
<LinearLayout    

xmlns:android="http://schemas.android.com/apk/res/android"  


android:orientation="vertical"    


android:layout_width="fill_parent"  


android:layout_height="fill_parent">   

    <ListView    

    android:id="@+id/list"    


    android:layout_width="fill_parent"  


    android:layout_height="fill_parent"    

       

    android:persistentDrawingCache="animation|scrolling"  


    android:layoutAnimation="@anim/layout_anim_ctrl" />   

</LinearLayout>  
public class ListActivity extends Activity {   
    
    String[] mListItems =    

    { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5"};   

    

    @Override  


    public void onCreate(Bundle savedInstanceState) {   


       super.onCreate(savedInstanceState);   

       setContentView(R.layout.list);   
    
       ArrayAdapter<String> listItemAdapter =    

           new ArrayAdapter<String>(this,   

              android.R.layout.simple_list_item_1, mListItems);   

       ListView lv = (ListView) this.findViewById(R.id.list);   

       lv.setAdapter(listItemAdapter);   
    }   
}  
class ViewAnimation extends Animation {   

       public ViewAnimation() {   

       }   
    

       @Override  


        public void initialize(int width, int height, int parentWidth, int parentHeight) {   


           super.initialize(width, height, parentWidth, parentHeight);   


           setDuration(2500);   


           setFillAfter(true);   


           setInterpolator(new LinearInterpolator());   

       }   
    

       @Override  


        protected void applyTransformation(float interpolatedTime,   

              Transformation t) {   

           final Matrix matrix = t.getMatrix();   

           matrix.setScale(interpolatedTime, interpolatedTime);   
              
       }   
    }  
public class ViewAnimActivity extends Activity {   
    
    Button mPlayBtn;   
    ImageView mAnimImage;   
    

    @Override  


    public void onCreate(Bundle savedInstanceState) {   


       super.onCreate(savedInstanceState);   

       setContentView(R.layout.view_anim_layout);   

       mAnimImage = (ImageView) this.findViewById(R.id.anim_image);   

    
       mPlayBtn = (Button) findViewById(R.id.play_btn);   

       mPlayBtn.setOnClickListener(new OnClickListener() {   

    

           @Override  


           public void onClick(View view) {   


              mAnimImage.startAnimation(new ViewAnimation());   

           }   
    
       });   
    
    }   
}  
<?xml version="1.0" encoding="utf-8"?>   
<LinearLayout    

xmlns:android="http://schemas.android.com/apk/res/android"  


android:orientation="vertical"  


android:layout_width="fill_parent"  


android:layout_height="fill_parent"  

>    
<Button   

android:id="@+id/play_btn"  


android:layout_width="fill_parent"  


android:layout_height="wrap_content"  


android:text="Start Animation"  

/>   
<ImageView    

android:id="@+id/anim_image"  


android:persistentDrawingCache="animation|scrolling"  


android:layout_width="fill_parent"    


android:layout_height="wrap_content"    


android:src="@drawable/ophone"  

/>   
</LinearLayout>  
class ViewAnimation extends Animation {   

       int mCenterX;//记录View的中间坐标   


       int mCenterY;   


       public ViewAnimation() {   

       }   
    

       @Override  


       public void initialize(int width, int height, int parentWidth, int parentHeight) {   


           super.initialize(width, height, parentWidth, parentHeight);   


           //初始化中间坐标值   


           mCenterX = width/2;    


           mCenterY = height/2;   


           setDuration(2500);   


           setFillAfter(true);   


           setInterpolator(new LinearInterpolator());   

       }   
    

       @Override  


       protected void applyTransformation(float interpolatedTime,   

              Transformation t) {   

           final Matrix matrix = t.getMatrix();   

           matrix.setScale(interpolatedTime, interpolatedTime);   

           //通过坐标变换,把参考点(0,0)移动到View中间   

           matrix.preTranslate(-mCenterX, -mCenterY);   

           //动画完成后再移回来   

           matrix.postTranslate(mCenterX, mCenterY);   
       }   
    }  
 
preTranslate函数是在缩放前移动而postTranslate是在缩放完成后移动。现在ImageView就是从中间出来的了。这样通过操作Matrix 可以实现各种复杂的变换。由于操作Matrix是实现动画变换的重点,这里简单介绍下Matrix的常用操作:
  • Reset():重置该矩阵
  • setScale():设置矩阵缩放
  • setTranslate():设置矩阵移动
  • setRotate():设置矩阵旋转
  • setSkew(): 使矩阵变形(扭曲)
 
矩阵也可以相乘,从线性代数中的矩阵运算中知道M1*M2 和M2*M1 是不一样的,所以在使用concat(m1,m2)函数的时候要注意顺序。
 
另外需要注意的是Matrix提供的API在OPhone1.0和OPhone1.5中是有变化的,请注意查看相关文档。
 
OPhone还提供了一个用来监听Animation事件的监听接口AnimationListener,如果你对Animatioin何时开始、何时结束、何时重复播放感兴趣则可以实现该接口。该接口提供了三个回调函数:onAnimationStart、onAnimationEnd、onAnimationRepeat。
 
使用Camera实现3D变换效果
最后来简单介绍下OPhone提供的android.graphics.Camera类,通过该类可以在2D条件下实现3D动画效果,该类可以看做一个视图显示的3D空间,然后可以在里面做各种操作。把上面的ViewAnimation修改为如下实现可以具体看看Camera的功能:
   
 class ViewAnimation extends Animation {   

       int mCenterX;//记录View的中间坐标   


       int mCenterY;   


       Camera camera = new Camera();   


       public ViewAnimation() {   

       }   
    

       @Override  


       public void initialize(int width, int height, int parentWidth,   


              int parentHeight) {   


           super.initialize(width, height, parentWidth, parentHeight);   


           //初始化中间坐标值   


           mCenterX = width/2;    


           mCenterY = height/2;   


           setDuration(2500);   


           setFillAfter(true);   


           setInterpolator(new LinearInterpolator());   

       }   
    

       @Override  


       protected void applyTransformation(float interpolatedTime,   

              Transformation t) {   

//         final Matrix matrix = t.getMatrix();   


//         matrix.setScale(interpolatedTime, interpolatedTime);   


//         //通过坐标变换,把参考点(0,0)移动到View中间   


//         matrix.preTranslate(-mCenterX, -mCenterY);   


//         //动画完成后再移回来   


//         matrix.postTranslate(mCenterX, mCenterY);   


           final Matrix matrix = t.getMatrix();   

           camera.save();   

           camera.translate(0.0f, 0.0f, (1300 - 1300.0f * interpolatedTime));   


           camera.rotateY(360 * interpolatedTime);   

           camera.getMatrix(matrix);   
           matrix.preTranslate(-mCenterX, -mCenterY);   
           matrix.postTranslate(mCenterX, mCenterY);   
           camera.restore();   
       }   
    }  
camera.translate(0.0f, 0.0f, (1300 - 1300.0f * interpolatedTime))在第一次调用的时候interpolatedTime值为0,相当于把ImageView在Z轴后移1300像素,然后逐步的往前移动到0,同时camera.rotateY(360 * interpolatedTime)函数又把ImageView沿Y轴翻转360度

相关推荐