基于OPhone 2.0的2D动画实践(二)

基于OPhone 2.0的2D动画实践(二)

OPhone平台开发, 2010-10-18 16:53:43

标签 : OPhone2.0 2D 动画

 

  本系列文章主要介绍了OPhone 2.0 SDK提供的两种实现2D动画的方式:帧动画和补间动画。文章的每个知识点都提供了精彩的实例以向读者展示2D动画的具体实现方法。通过对本系列文章的学习,读者可利用2D动画实现非常绚丽的界面效果。
 

  补间动画简介
  如果动画中的图像变换比较有规律时,可以采用自动生成中间图像的方式来生成动画。例如,图像的移动、旋转、缩放等。当然,还有更复杂的情况,例如,由正方形变成圆形、圆形变成椭圆形,这些变化过程中的图像都可以根据一定的数学算法自动生成。而我们只需要指定动画的第1帧和最后一帧的图像即可。这种自动生成中间图像的动画被称为补间(Tween)动画。
  补间动画的优点是节省硬盘空间。这是因为这种动画只需要提供两帧图像(第1帧和最后一帧),其他的图像都由系统自动生成。当然,这种动画也有一定的缺点,就是动画很复杂时无法自动生成中间图像,例如,由电影画面组件的动画,由于每幅画面过于复杂,系统无法预料下一幅画面是什么样子。因此,这种复杂的动画只能使用帧动画来完成。在本节将介绍OPhone SDK提供的4种补间动画效果:移动、缩放、旋转和透明度。OPhone SDK并未提供更复杂的补间动画。如果要实现更复杂的补间动画,需要开发人员自已编码来完成。
 

  移动补间动画
  移动是最常见的动画效果。我们可以通过配置动画文件(xml文件)或Java代码来实现补间动画的移动效果。补间动画文件需要放在res\anim目录中。在动画文件中通过<translate>标签设置移动效果。假设在res\anim目录下有一个动画文件:test.xml,该文件的内容如下:

  1. <translate xmlns:android="http://schemas.android.com/apk/res/android"  
  2.   android:interpolator="@android:anim/accelerate_interpolator"  
  3.   android:fromXDelta="0" android:toXDelta="320" android:fromYDelta="0"  
  4.   android:toYDelta="0" android:duration="2000" />  
<translate xmlns:android="http://schemas.android.com/apk/res/android"   android:interpolator="@android:anim/accelerate_interpolator"   android:fromXDelta="0" android:toXDelta="320" android:fromYDelta="0"   android:toYDelta="0" android:duration="2000" />


  从上面的配置代码可以看出,<translate>标签中设置了6个属性,这6个属性的含义如下:
   android:interpolator:表示动画渲染器。通过android:interpolator属性可以设置3个动画渲染器:accelerate_interpolator(动画加速器)、decelerate_interpolator(动画减速器)和accelerate_decelerate_interpolator(动画加速减速器)。动画加速器使动画在开始时速度最慢,然后逐渐加速。动画减速器使动画在开始时速度最快,然后逐渐减速。动画加速减速器使动画在开始和结束时速度最慢,但在前半部分时开始加速,在后半部分时开始减速。
   android:fromXDelta:动画起始位置的横坐标。
   android:toXDelta:动画结束位置的横坐标。
   android:fromXDelta:动画起始位置的纵坐标。
   android:toYDelta:动画结束位置的纵坐标。
   android:duration:动画的持续时间。单位是毫秒。也就是说,动画要在android:duration属性指定的时间内从起始点移动到结束点。


  装载补间动画文件需要使用android.view.animation.AnimationUtils. loadAnimation方法,该方法的定义如下:
 

  1. public static Animation loadAnimation(Context context, int id);  
public static Animation loadAnimation(Context context, int id);

  其中id表示动画文件的资源ID。装载test.xml文件的代码如下:
 

  1. Animation animation = AnimationUtils.loadAnimation(this, R.anim.test);  
Animation animation = AnimationUtils.loadAnimation(this, R.anim.test);

  假设有一个EditText组件(editText),将test.xml文件中设置的补间动画应用到EditText组件上的方式有如下两种:
  1.  使用EditText类的startAnimation方法,代码如下:

  1. editText.startAnimation(animation);  
editText.startAnimation(animation);


  2.  使用Animation类的start方法,代码如下:
 

  1. //  绑定补间动画   
  2.   editText.setAnimation(animation);   
  3.   //  开始动画   
  4.   animation.start();  
// 绑定补间动画   editText.setAnimation(animation);   // 开始动画   animation.start();


  使用上面两种方式开始补间动画都只显示一次。如果想循环显示动画,需要使用如下的代码将动画设置成循环状态。

  1. animation.setRepeatCount(Animation.INFINITE);  
animation.setRepeatCount(Animation.INFINITE);


  上面两行代码在开始动画之前和之后执行都没有问题。

  移动补间动画的实例
  本例的动画效果是在屏幕上方的EditText组件从左到右循环匀速水平移动。EditText下方的小球上下移动。从上到下移动时加速。从下到上移动时减速。
  本例涉及到3个动画渲染器:accelerate_interpolator、decelerate_interpolator和linear_interpolator。其中前两个动画渲染器可以直接作为android:interpolator属性的值,而linear_interpolator虽然在系统中已定义,但由于不是public的,因此,需要自己定义linear_interpolator.xml文件。当然,也可以将系统的linear_interpolator.xml文件复制到Eclipse工程中的res\anim目录下。
  在本例中定义了3个动画文件,其中translate_right.xml被应用于EditText组件。translate_bottom.xml(从上到下移动,加速)和translate_top.xml(从下到上移动,减速)被应用于小球(ImageView组件)。这3个动画文件的内容如下:

  1. translate_right.xml   
  2.   <translate xmlns:android="http://schemas.android.com/apk/res/android"  
  3.   android:interpolator="@anim/linear_interpolator"  
  4.   android:fromXDelta="-320" android:toXDelta="320" android:fromYDelta="0"  
  5.   android:toYDelta="0" android:duration="5000" />   
  6.   
  7. translate_bottom.xml   
  8.   <translate xmlns:android="http://schemas.android.com/apk/res/android"  
  9.    android:interpolator="@android:anim/accelerate_interpolator"  
  10.    android:fromXDelta="0" android:toXDelta="0" android:fromYDelta="0"  
  11.    android:toYDelta="260" android:duration="2000" />   
  12. translate_top.xml   
  13.   <translate xmlns:android="http://schemas.android.com/apk/res/android"  
  14.   android:interpolator="@android:anim/decelerate_interpolator"  
  15.   android:fromXDelta="0" android:toXDelta="0" android:fromYDelta="260"  
  16.   android:toYDelta="0" android:duration="2000" />  
translate_right.xml   <translate xmlns:android="http://schemas.android.com/apk/res/android"   android:interpolator="@anim/linear_interpolator"   android:fromXDelta="-320" android:toXDelta="320" android:fromYDelta="0"   android:toYDelta="0" android:duration="5000" /> translate_bottom.xml   <translate xmlns:android="http://schemas.android.com/apk/res/android"    android:interpolator="@android:anim/accelerate_interpolator"    android:fromXDelta="0" android:toXDelta="0" android:fromYDelta="0"    android:toYDelta="260" android:duration="2000" /> translate_top.xml   <translate xmlns:android="http://schemas.android.com/apk/res/android"   android:interpolator="@android:anim/decelerate_interpolator"   android:fromXDelta="0" android:toXDelta="0" android:fromYDelta="260"   android:toYDelta="0" android:duration="2000" />


  EditText组件的循环水平移动可以直接使用setRepeatMode和setRepeatCount方法进行设置。而小球的移动需要应用两个动画文件。本例采用的方法是在一个动画播放完后,再将另一个动画文件应用到显示小球的ImageView组件中。这个操作需要在AnimationListener接口的onAnimationEnd方法中完成。
  运行本例后,单击【开始动画】按钮后,EditText组件从屏幕的左侧出来,循环水平向右移动,当EditText组件完全移进屏幕右侧时,会再次从屏幕左侧出来。同时小球会上下移动。效果如图1所示。

  图1  移动补间动画

  本例的完整代码如下:

  1. package net.blogjava.mobile;   
  2.   
  3.   import android.app.Activity;   
  4.   import android.os.Bundle;   
  5.   import android.view.View;   
  6.   import android.view.View.OnClickListener;   
  7.   import android.view.animation.Animation;   
  8.   import android.view.animation.AnimationUtils;   
  9.   import android.view.animation.Animation.AnimationListener;   
  10.   import android.widget.Button;   
  11.   import android.widget.EditText;   
  12.   import android.widget.ImageView;   
  13.   
  14.   public class Main extends Activity implements OnClickListener, AnimationListener   
  15.   {   
  16.   private EditText editText;   
  17.   private ImageView imageView;   
  18.   private Animation animationRight;   
  19.   private Animation animationBottom;   
  20.   private Animation animationTop;   
  21.   
  22.   //  animation参数表示当前应用到组件上的Animation对象   
  23.   @Override  
  24.   public void onAnimationEnd(Animation animation)   
  25.   {   
  26.   //  根据当前显示的动画决定下次显示哪一个动画   
  27.   if (animation.hashCode() == animationBottom.hashCode())   
  28.   imageView.startAnimation(animationTop);   
  29.   else if (animation.hashCode() == animationTop.hashCode())   
  30.   imageView.startAnimation(animationBottom);   
  31.   }   
  32.   @Override  
  33.   public void onAnimationRepeat(Animation animation)   
  34.   {   
  35.   }   
  36.   @Override  
  37.   public void onAnimationStart(Animation animation)   
  38.   {   
  39.   }   
  40.   @Override  
  41.   public void onClick(View view)   
  42.   {   
  43.   //  开始EditText的动画   
  44.   editText.setAnimation(animationRight);   
  45.   animationRight.start();   
  46.   animationRight.setRepeatCount(Animation.INFINITE);   
  47.   editText.setVisibility(EditText.VISIBLE);   
  48.   //  开始小球的动画   
  49.   imageView.startAnimation(animationBottom);   
  50.   }   
  51.   @Override  
  52.   public void onCreate(Bundle savedInstanceState)   
  53.   {   
  54.   super.onCreate(savedInstanceState);   
  55.   setContentView(R.layout.main);   
  56.   editText = (EditText) findViewById(R.id.edittext);   
  57.   editText.setVisibility(EditText.INVISIBLE);   
  58.   Button button = (Button) findViewById(R.id.button);   
  59.   button.setOnClickListener(this);   
  60.   imageView = (ImageView) findViewById(R.id.imageview);   
  61.   animationRight = AnimationUtils.loadAnimation(this,R.anim.translate_right);   
  62.   animationBottom = AnimationUtils.loadAnimation(this,R.anim.translate_bottom);   
  63.   animationTop = AnimationUtils.loadAnimation(this, R.anim.translate_top);   
  64.   animationBottom.setAnimationListener(this);   
  65.   animationTop.setAnimationListener(this);   
  66.   }   
  67.   }  
package net.blogjava.mobile;   import android.app.Activity;   import android.os.Bundle;   import android.view.View;   import android.view.View.OnClickListener;   import android.view.animation.Animation;   import android.view.animation.AnimationUtils;   import android.view.animation.Animation.AnimationListener;   import android.widget.Button;   import android.widget.EditText;   import android.widget.ImageView;   public class Main extends Activity implements OnClickListener, AnimationListener   {   private EditText editText;   private ImageView imageView;   private Animation animationRight;   private Animation animationBottom;   private Animation animationTop;   // animation参数表示当前应用到组件上的Animation对象   @Override   public void onAnimationEnd(Animation animation)   {   // 根据当前显示的动画决定下次显示哪一个动画   if (animation.hashCode() == animationBottom.hashCode())   imageView.startAnimation(animationTop);   else if (animation.hashCode() == animationTop.hashCode())   imageView.startAnimation(animationBottom);   }   @Override   public void onAnimationRepeat(Animation animation)   {   }   @Override   public void onAnimationStart(Animation animation)   {   }   @Override   public void onClick(View view)   {   // 开始EditText的动画   editText.setAnimation(animationRight);   animationRight.start();   animationRight.setRepeatCount(Animation.INFINITE);   editText.setVisibility(EditText.VISIBLE);   // 开始小球的动画   imageView.startAnimation(animationBottom);   }   @Override   public void onCreate(Bundle savedInstanceState)   {   super.onCreate(savedInstanceState);   setContentView(R.layout.main);   editText = (EditText) findViewById(R.id.edittext);   editText.setVisibility(EditText.INVISIBLE);   Button button = (Button) findViewById(R.id.button);   button.setOnClickListener(this);   imageView = (ImageView) findViewById(R.id.imageview);   animationRight = AnimationUtils.loadAnimation(this,R.anim.translate_right);   animationBottom = AnimationUtils.loadAnimation(this,R.anim.translate_bottom);   animationTop = AnimationUtils.loadAnimation(this, R.anim.translate_top);   animationBottom.setAnimationListener(this);   animationTop.setAnimationListener(this);   }   }


  缩放补间动画
  通过<scale>标签可以定义缩放补间动画。下面的代码定义了一个标准的缩放补间动画。

  1. <scale xmlns:android="http://schemas.android.com/apk/res/android"  
  2.   android:interpolator="@android:anim/decelerate_interpolator"  
  3.   android:fromXScale="0.2" android:toXScale="1.0" android:fromYScale="0.2"  
  4.   android:toYScale="1.0" android:pivotX="50%" android:pivotY="50%"  
  5.   android:duration="2000" />  
<scale xmlns:android="http://schemas.android.com/apk/res/android"   android:interpolator="@android:anim/decelerate_interpolator"   android:fromXScale="0.2" android:toXScale="1.0" android:fromYScale="0.2"   android:toYScale="1.0" android:pivotX="50%" android:pivotY="50%"   android:duration="2000" />


  <scale>标签和<translate>标签中有些属性是相同的,而有些属性是<scale>标签特有的,这些属性的含义如下:
   android:fromXScale:表示沿X轴缩放的起始比例。
   android:toXScale:表示沿X轴缩放的结束比例。
   android:fromYScale:表示沿Y轴缩放的起始比例。
   android:toYScale:表示沿Y轴缩放的结束比例。
   android:pivotX:表示沿X轴方向缩放的支点位置。如果该属性值为"50%",则支点在沿X轴中心的位置。
   android:pivotY:表示沿Y轴方向缩放的支点位置。如果该属性值为"50%",则支点在沿Y轴中心的位置。
  其中前4个属性的取值规则如下:
   0.0:表示收缩到没有。
   1.0:表示正常不收缩。
   大于1.0:表示将组件放大到相应的比例。例如,值为1.5,表示放大到原组件的1.5倍。
   小于1.0:表示将组件缩小到相应的比例。例如,值为0.5,表示缩小到原组件的50%。
  如果想通过Java代码实现缩放补间动画,可以创建android.view.animation.ScaleAnimation对象。ScaleAnimation类构造方法的定义如下:
  public ScaleAnimation(float fromX, float toX, float fromY, float toY,float pivotX, float pivotY)
  通过ScaleAnimation类的构造方法可以设置上述6个属性值。设置其他属性的方法与移动补间动画相同。


  总结
  本文主要两种补间动画:移动补间动画和缩放补间动画,并给出了相应的实例。在下一篇文章中将会介绍另外两种补间动画。


  作者介绍
  李宁,东北大学计算机专业硕士,拥有超过10年的软件开发经验。曾任国内某知名企业项目经理;目前担任eoeandroid和ophonesdn版主;中国移动开发者社区OPhone专家;51CTO客作专家;CSDN博客专家。曾领导并参与开发了多个大中型项目。目前主要从事Android及其相关产品的研发。从2005年进入写作领域以来,为《程序员》、《电脑编程技巧与维护》、《电脑报》、IT168、天极网等平面媒体和网络媒体撰写了一百多篇原创技术和评论文章。并在个人blog(http://nokiaguy.blogjava.net)上发表了大量的原创技术文章。2007年获《电脑编程技巧与维护》优秀作者。2009年获得OPhone征文大赛二等奖。个人著作:《Android/OPhone开发完全讲义》、《人人都玩开心网:Ext JS+Android+SSH整合开发Web与移动SNS》、《Java Web开发速学宝典》。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值