Android提供了3种动画类型:View Animation 、Drawable Animation 、Property Animation
Drawable Animations就是很多书籍中提到的逐帧动画(frame-by-frame animation)
1.Drawable Animation
定义动画的每个图片,定义在xml文件里面:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <animation-list xmlns:android="http://schemas.android.com/apk/res/android" 3 android:oneshot="false"> 4 <item android:duration="100"> 5 <layer-list> 6 <item android:drawable="@drawable/login_loading_00" /> 7 <item android:drawable="@drawable/login_loading_10" /> 8 </layer-list> 9 </item> 10 <item android:duration="100"> 11 <layer-list> 12 <item android:drawable="@drawable/login_loading_01" /> 13 <item android:drawable="@drawable/login_loading_11" /> 14 </layer-list> 15 </item> 16 <item android:duration="100"> 17 <layer-list> 18 <item android:drawable="@drawable/login_loading_02" /> 19 <item android:drawable="@drawable/login_loading_12" /> 20 </layer-list> 21 </item> 22 <item android:duration="100"> 23 <layer-list> 24 <item android:drawable="@drawable/login_loading_03" /> 25 <item android:drawable="@drawable/login_loading_13" /> 26 </layer-list> 27 </item> 28 <item android:duration="100"> 29 <layer-list> 30 <item android:drawable="@drawable/login_loading_04" /> 31 <item android:drawable="@drawable/login_loading_14" /> 32 </layer-list> 33 </item> 34 <item android:duration="100"> 35 <layer-list> 36 <item android:drawable="@drawable/login_loading_05" /> 37 <item android:drawable="@drawable/login_loading_15" /> 38 </layer-list> 39 </item> 40 <item android:duration="100"> 41 <layer-list> 42 <item android:drawable="@drawable/login_loading_06" /> 43 <item android:drawable="@drawable/login_loading_16" /> 44 </layer-list> 45 </item> 46 <item android:duration="100"> 47 <layer-list> 48 <item android:drawable="@drawable/login_loading_07" /> 49 <item android:drawable="@drawable/login_loading_17" /> 50 </layer-list> 51 </item> 52 <item android:duration="100"> 53 <layer-list> 54 <item android:drawable="@drawable/login_loading_08" /> 55 <item android:drawable="@drawable/login_loading_18" /> 56 </layer-list> 57 </item> 58 <item android:duration="100"> 59 <layer-list> 60 <item android:drawable="@drawable/login_loading_09" /> 61 <item android:drawable="@drawable/login_loading_19" /> 62 </layer-list> 63 </item> 64 </animation-list>
定义主函数,调用此动画:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.example.drawableanimationdemo; 2 3 import android.app.Activity; 4 import android.graphics.drawable.AnimationDrawable; 5 import android.os.Bundle; 6 import android.view.MotionEvent; 7 import android.view.View; 8 import android.view.View.OnClickListener; 9 import android.view.Window; 10 import android.widget.Button; 11 import android.widget.ImageView; 12 13 public class MainActivity extends Activity { 14 15 private AnimationDrawable loadingAnimation; 16 private Button bt; 17 boolean isStop; 18 @Override 19 protected void onCreate(Bundle savedInstanceState) { 20 super.onCreate(savedInstanceState); 21 requestWindowFeature(Window.FEATURE_NO_TITLE); 22 setContentView(R.layout.activity_main); 23 //将该逐帧xml文件设置为ImageView的背景 24 bt=(Button) findViewById(R.id.bt); 25 ImageView loadingImg = (ImageView) findViewById(R.id.loading); 26 loadingImg.setBackgroundResource(R.drawable.loading); 27 loadingAnimation = (AnimationDrawable) loadingImg.getBackground(); 28 isStop=true; 29 bt.setText("开始"); 30 bt.setOnClickListener(new OnClickListener() { 31 32 @Override 33 public void onClick(View v) { 34 // TODO Auto-generated method stub 35 isStop=!isStop; 36 if(isStop){ 37 loadingAnimation.stop(); 38 bt.setText("开始"); 39 } 40 else{ 41 loadingAnimation.start(); 42 bt.setText("停止"); 43 } 44 } 45 }); 46 } 47 48 }
2.View Animation
2.1简介
View animation只能应用于View对象,而且只支持一部分属性,如支持缩放旋转而不支持背景颜色的改变。
而且对于View animation,它只是改变了View对象绘制的位置,而没有改变View对象本身,比如,你有一个Button,坐标(100,100),Width:200,Height:50,而你有一个动画使其变为Width:100,Height:100,你会发现动画过程中触发按钮点击的区域仍是(100,100)-(300,150)。
View Animation就是一系列View形状的变换,如大小的缩放,透明度的改变,位置的改变,动画的定义既可以用代码定义也可以用XML定义,当然,建议用XML定义。
可以给一个View同时设置多个动画,比如从透明至不透明的淡入效果,与从小到大的放大效果,这些动画可以同时进行,也可以在一个完成之后开始另一个。
用XML定义的动画放在/res/anim/文件夹内,XML文件的根元素可以为<alpha>,<scale>,<translate>,<rotate>,interpolator元素或<set>(表示以上几个动画的集合,set可以嵌套)。默认情况下,所有动画是同时进行的,可以通过startOffset属性设置各个动画的开始偏移(开始时间)来达到动画顺序播放的效果。
可以通过设置interpolator属性改变动画渐变的方式,如AccelerateInterpolator,开始时慢,然后逐渐加快。默认为AccelerateDecelerateInterpolator。
类图
2.2java代码中定义animation
构造函数
AlphaAnimation(Context context, AttributeSet attrs)
AlphaAnimation(float fromAlpha, float toAlpha) RotateAnimation(Context context, AttributeSet attrs) RotateAnimation(float fromDegrees, float toDegrees) RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY) //旋转中心 RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) //定位方式以及定义旋转中心 ScaleAnimation(Context context, AttributeSet attrs) ScaleAnimation(float fromX, float toX, float fromY, float toY) ScaleAnimation(float fromX, float toX, float fromY, float toY, float pivotX, float pivotY) ScaleAnimation(float fromX, float toX, float fromY, float toY, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)
//定位方式以及定义中心
TranslateAnimation(Context context, AttributeSet attrs) TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue, int fromYType, float fromYValue, int toYType, float toYValue) //定位方式以及定义中心
AnimationSet(Context context, AttributeSet attrs)
AnimationSet(boolean shareInterpolator)
上面的第一个构造函数都是通过xml定义animation的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class MainActivity extends Activity implements OnClickListener{ 2 3 private Button scale; 4 private Button alpha; 5 private Button rotation; 6 private Button translate; 7 private Button animationSet; 8 // Alpha动画 - 渐变透明度 9 private Animation alphaAnimation = null; 10 11 // Sacle动画 - 渐变尺寸缩放 12 private Animation scaleAnimation = null; 13 14 // Translate动画 - 位置移动 15 private Animation translateAnimation = null; 16 17 // Rotate动画 - 画面旋转 18 private Animation rotateAnimation = null; 19 20 @Override 21 protected void onCreate(Bundle savedInstanceState) { 22 super.onCreate(savedInstanceState); 23 requestWindowFeature(Window.FEATURE_NO_TITLE); 24 setContentView(R.layout.activity_main); 25 // 将该逐帧xml文件设置为ImageView的背景 26 scale = (Button) findViewById(R.id.bt0); 27 scale.setOnClickListener(this); 28 alpha = (Button) findViewById(R.id.bt1); 29 alpha.setOnClickListener(this); 30 rotation = (Button) findViewById(R.id.bt2); 31 rotation.setOnClickListener(this); 32 translate = (Button) findViewById(R.id.bt3); 33 translate.setOnClickListener(this); 34 animationSet = (Button) findViewById(R.id.bt4); 35 animationSet.setOnClickListener(this); 36 } 37 38 @Override 39 public void onClick(View v) { 40 switch(v.getId()){ 41 case R.id.bt0: //缩放 42 scaleAnimation = new ScaleAnimation(0.1f, 1f, 0.1f, 1f,RotateAnimation.RELATIVE_TO_SELF,0.5f,RotateAnimation.RELATIVE_TO_SELF,0.5f); 43 scaleAnimation.setDuration(500); 44 scaleAnimation.setRepeatCount(Animation.INFINITE); 45 v.startAnimation(scaleAnimation); 46 break; 47 case R.id.bt1: //透明 48 alphaAnimation = new AlphaAnimation(0.1f, 1.0f); 49 alphaAnimation.setDuration(3000); 50 v.startAnimation(alphaAnimation); 51 alphaAnimation.setRepeatCount(Animation.INFINITE); 52 break; 53 case R.id.bt2: //旋转 54 rotateAnimation = new RotateAnimation(0f, 360f,RotateAnimation.RELATIVE_TO_SELF,0.5f,RotateAnimation.RELATIVE_TO_SELF,0.5f); 55 rotateAnimation.setDuration(1000); 56 v.startAnimation(rotateAnimation); 57 rotateAnimation.setRepeatCount(Animation.INFINITE); 58 break; 59 case R.id.bt3: //平移 60 translateAnimation = new TranslateAnimation(0.1f, 100.0f,0.1f,100.0f); 61 translateAnimation.setDuration(1000); 62 v.startAnimation(translateAnimation); 63 translateAnimation.setRepeatCount(Animation.INFINITE); 64 break; 65 case R.id.bt4: //动画集 66 translateAnimation = new TranslateAnimation(0.1f, 100.0f,0.1f,100.0f); 67 alphaAnimation = new AlphaAnimation(0.1f, 1.0f); 68 rotateAnimation = new RotateAnimation(0f, 360f); 69 AnimationSet set = new AnimationSet(true); 70 set.addAnimation(translateAnimation); 71 set.addAnimation(rotateAnimation); 72 set.addAnimation(alphaAnimation); 73 set.setDuration(1000); 74 v.startAnimation(set); 75 break; 76 } 77 } 78 79 }
2.3xml代码中定义animation
以scale为例,其他类似
scale标签是缩放动画,可以实现动态调控件尺寸的效果,有下面几个属性:
- android:fromXScale 起始的X方向上相对自身的缩放比例,浮点值,比如1.0代表自身无变化,0.5代表起始时缩小一倍,2.0代表放大一倍;
- android:toXScale 结尾的X方向上相对自身的缩放比例,浮点值;
- android:fromYScale 起始的Y方向上相对自身的缩放比例,浮点值,
- android:toYScale 结尾的Y方向上相对自身的缩放比例,浮点值;
- android:pivotX 缩放起点X轴坐标,可以是数值、百分数、百分数p 三种样式,比如 50、50%、50%p,当为数值时,表示在当前View的左上角,即原点处加上50px,做为起始缩放点;如果是50%,表示在当前控件的左上角加上自己宽度的50%做为起始点;如果是50%p,那么就是表示在当前的左上角加上父控件宽度的50%做为起始点x轴坐标。(具体意义,后面会举例演示)
- android:pivotY 缩放起点Y轴坐标,取值及意义跟android:pivotX一样。
从Animation类继承的属性:
- android:duration 动画持续时间,以毫秒为单位
- android:fillAfter 如果设置为true,控件动画结束时,将保持动画最后时的状态
- android:fillBefore 如果设置为true,控件动画结束时,还原到开始动画前的状态
- android:fillEnabled 与android:fillBefore 效果相同,都是在动画结束时,将控件还原到初始化状态
- android:repeatCount 重复次数
- android:repeatMode 重复类型,有reverse和restart两个值,reverse表示倒序回放,restart表示重新放一遍,必须与repeatCount一起使用才能看到效果。因为这里的意义是重复的类型,即回放时的动作。
- android:interpolator 设定插值器,其实就是指定的动作效果,比如弹跳效果等,不在这小节中讲解,后面会单独列出一单讲解。
1 <?xml version="1.0" encoding="utf-8"?> 2 <scale xmlns:android="http://schemas.android.com/apk/res/android" 3 android:fromXScale="0.0" 4 android:toXScale="1.4" 5 android:fromYScale="0.0" 6 android:toYScale="1.4" 7 android:pivotX="50" 8 android:pivotY="50" 9 android:duration="700" />
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="3000" android:fillAfter="true"> <alpha android:fromAlpha="0.0" android:toAlpha="1.0"/> <scale android:fromXScale="0.0" android:toXScale="1.4" android:fromYScale="0.0" android:toYScale="1.4" android:pivotX="50%" android:pivotY="50%"/> <rotate android:fromDegrees="0" android:toDegrees="720" android:pivotX="50%" android:pivotY="50%"/> </set>
在java代码中调用:
1 scaleAnimation = AnimationUtils.loadAnimation(this, R.anim.scaleanim);
2.4Interpolator插值器
定义动画速率
- AccelerateDecelerateInterpolator 在动画开始与介绍的地方速率改变比较慢,在中间的时候加速
- AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速
- AnticipateInterpolator 开始的时候向后然后向前甩
- AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值
- BounceInterpolator 动画结束的时候弹起
- CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
- DecelerateInterpolator 在动画开始的地方快然后慢
- LinearInterpolator 以常量速率改变
- OvershootInterpolator 向前甩一定值后再回到原来位置
2.5 通过AnimationSet应用多个动画
AnimationSet类是Android系统中的动画集合类,用于控制View对象进行多个动作的组合,该类继承于Animation类。AnimationSet类中的很多方法都与Animation类一致,该类中最常用的方法便是addAnimation方法,该方法用于为动画集合对象添加动画对象。
1 AnimationSet set=new AnimationSet(true); //创建动画集对象 2 set.addAnimation(translateAnimation); //添加位置变化动画 3 set.addAnimation(scaleAnimation); //添加尺寸变化动画 4 set.addAnimation(alphaAnimation); //添加透明度渐变动画 5 set.setFillAfter(true); //停留在最后的位置 6 set.setFillEnabled(true); 7 image.setAnimation(set); //设置动画 8 set.startNow(); //启动动画
3.Property Animation
属性动画,它更改的是对象的实际属性,在View Animation(Tween Animation)中,其改变的是View的绘制效果,真正的View的属性保持不变,比如无论你在对话中如何缩放Button的大小,Button的有效点击区域还是没有应用动画时的区域,其位置与大小都不变。而在Property Animation中,改变的是对象的实际属性,如Button的缩放,Button的位置与大小属性值都改变了。而且Property Animation不止可以应用于View,还可以应用于任何对象。Property Animation只是表示一个值在一段时间内的改变,当值改变时要做什么事情完全是你自己决定的。
在Property Animation中,可以对动画应用以下属性:
- Duration:动画的持续时间
- TimeInterpolation:属性值的计算方式,如先快后慢
- TypeEvaluator:根据属性的开始、结束值与TimeInterpolation计算出的因子计算出当前时间的属性值
- Repeat Count and behavoir:重复次数与方式,如播放3次、5次、无限循环,可以此动画一直重复,或播放完时再反向播放
- Animation sets:动画集合,即可以同时对一个对象应用几个动画,这些动画可以同时播放也可以对不同动画设置不同开始偏移
- Frame refreash delay:多少时间刷新一次,即每隔多少时间计算一次属性值,默认为10ms,最终刷新时间还受系统进程调度与硬件的影响
3.1类图
ObjectAnimator 动画的执行类,后面详细介绍
ValueAnimator 动画的执行类,后面详细介绍
AnimatorSet 用于控制一组动画的执行:线性,一起,每个动画的先后执行等。
AnimatorInflater 用户加载属性动画的xml文件
TypeEvaluator 类型估值,主要用于设置动画操作属性的值。
TimeInterpolator 时间插值,上面已经介绍。
总的来说,属性动画就是,动画的执行类来设置动画操作的对象的属性、持续时间,开始和结束的属性值,时间差值等,然后系统会根据设置的参数动态的变化对象的属性。
3.2 ObjectAnimator
1、提供了ofInt、ofFloat、ofObject,这几个方法都是设置动画作用的元素、作用的属性、动画开始、结束、以及中间的任意个属性值。
当对于属性值,只设置一个的时候,会认为当前对象该属性的值为开始(getPropName反射获取),然后设置的值为终点。如果设置两个,则一个为开始、一个为结束~~~
动画更新的过程中,会不断调用setPropName更新元素的属性,所有使用ObjectAnimator更新某个属性,必须得有getter(设置一个属性值的时候)和setter方法~
2、如果你操作对象的该属性方法里面,比如上例的setRotationX如果内部没有调用view的重绘,则你需要自己按照下面方式手动调用。
1 anim.addUpdateListener(new AnimatorUpdateListener() 2 { 3 @Override 4 public void onAnimationUpdate(ValueAnimator animation) 5 { 6 // view.postInvalidate(); 7 // view.invalidate(); 8 } 9 });
直接上例子:
1 package com.example.drawableanimationdemo; 2 3 import android.animation.ObjectAnimator; 4 import android.animation.PropertyValuesHolder; 5 import android.app.Activity; 6 import android.graphics.drawable.AnimationDrawable; 7 import android.os.Bundle; 8 import android.util.Log; 9 import android.view.MotionEvent; 10 import android.view.View; 11 import android.view.View.OnClickListener; 12 import android.view.Window; 13 import android.view.animation.*; 14 import android.widget.Button; 15 import android.widget.ImageView; 16 17 public class MainActivity extends Activity implements OnClickListener { 18 19 private Button scale; 20 private Button rotation; 21 private Button translate; 22 23 @Override 24 protected void onCreate(Bundle savedInstanceState) { 25 super.onCreate(savedInstanceState); 26 requestWindowFeature(Window.FEATURE_NO_TITLE); 27 setContentView(R.layout.activity_main); 28 scale = (Button) findViewById(R.id.bt0); 29 scale.setOnClickListener(this); 30 31 rotation = (Button) findViewById(R.id.bt2); 32 rotation.setOnClickListener(this); 33 translate = (Button) findViewById(R.id.bt1); 34 translate.setOnClickListener(this); 35 } 36 37 @Override 38 public void onClick(View v) { 39 switch (v.getId()) { 40 case R.id.bt0: // 缩放 41 ObjectAnimator o = ObjectAnimator.ofFloat(v, "scaleX", 0.1f, 1.0f); 42 o.setRepeatCount(5); 43 o.setDuration(3000); 44 o.start(); 45 break; 46 case R.id.bt2: // 旋转 47 PropertyValuesHolder p1 = PropertyValuesHolder.ofFloat("alpha", 48 0.1f, 0.9f); 49 PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat("rotation", 50 0, 360); 51 PropertyValuesHolder p4 = PropertyValuesHolder.ofFloat("scaleX", 52 0, 1); 53 PropertyValuesHolder p5 = PropertyValuesHolder.ofFloat("scaleY", 54 0, 1); 55 ObjectAnimator o1 = ObjectAnimator 56 .ofPropertyValuesHolder(v, p1, p2,p4,p5); 57 o1.setRepeatCount(5); 58 o1.setDuration(5000).start(); 59 break; 60 case R.id.bt1: // 平移 61 ObjectAnimator o2 = ObjectAnimator.ofFloat(v, "translationX", 20f, 62 300f); 63 o2.setRepeatCount(5); 64 o2.setRepeatMode(ObjectAnimator.REVERSE); 65 o2.setDuration(3000); 66 o2.setInterpolator(new BounceInterpolator()); 67 o2.start(); 68 break; 69 } 70 } 71 }
其中用到了PropertyValuesHolder类,用作组合设计。
3.3 ValueAnimator
ValueAnimator包含Property Animation动画的所有核心功能,如动画时间,开始、结束属性值,相应时间属性值计算方法等。应用Property Animation有两个步聚:
- 计算属性值
- 根据属性值执行相应的动作,如改变对象的某一属性。
ValuAnimiator只完成了第一步工作,如果要完成第二步,需要实现ValueAnimator.onUpdateListener接口,这个接口只有一个函数onAnimationUpdate(),在这个函数中会传入ValueAnimator对象做为参数,通过这个ValueAnimator对象的getAnimatedValue()函数可以得到当前的属性值如:
1 ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f); 2 animation.setDuration(1000); 3 animation.addUpdateListener(new AnimatorUpdateListener() { 4 @Override 5 public void onAnimationUpdate(ValueAnimator animation) { 6 Log.i("update", ((Float) animation.getAnimatedValue()).toString());//在这里改变对象的属性 7 invalidate(); //刷新对象的属性,否则不会调用重新绘制 8 } 9 }); 10 animation.setInterpolator(new CycleInterpolator(3)); 11 animation.start();
3.4 监听器
Animator.AnimatorListener:
1 onAnimationStart() 2 onAnimationEnd() 3 onAnimationRepeat() 4 //当动画被取消时调用,同时会调用onAnimationEnd(). 5 onAnimationCancel()
ValueAnimator.AnimatorUpdateListener
onAnimationUpdate() //通过监听这个事件在属性的值更新时执行相应的操作,对于ValueAnimator一般要监听此事件执行相应的动作,不然Animation没意义,在ObjectAnimator(继承自ValueAnimator)中会自动更新属性,如无必要不必监听。在函数中会传递一个ValueAnimator参数,通过此参数的getAnimatedValue()取得当前动画属性值。
AnimatorListenerAdapter
AnimatorListenerAdapter继承了AnimatorListener接口,然后空实现了所有的方法。可以继承AnimatorListenerAdapter而不是实现AnimatorListener接口来简化操作,这样我们就只用定义想监听的事件而不用实现每个函数却只定义一空函数体。
1 ObjectAnimator oa=ObjectAnimator.ofFloat(tv, "alpha", 0f, 1f); 2 oa.setDuration(3000); 3 oa.addListener(new AnimatorListenerAdapter(){ 4 public void on AnimationEnd(Animator animation){ 5 Log.i("Animation","end"); 6 } 7 }); 8 oa.start();
3.5 通过AnimatorSet应用多个动画
AnimatorSet提供了一个把多个动画组合成一个组合的机制,并可设置组中动画的时序关系,如同时播放,顺序播放等。我们最常用的是调用其play、before、with、after 等方法设置动画的执行顺序。
1 //使用playTogether两个动画同时执行,当然还有playSequentially依次执行~~ 2 void playSequentially(Animator... items) 3 void playSequentially(List<Animator> items) 4 void playTogether(Collection<Animator> items) 5 void playTogether(Animator... items)
1 AnimatorSet bouncer = new AnimatorSet(); 2 bouncer.play(anim1).before(anim2); 3 bouncer.play(anim2).with(anim3); 4 bouncer.play(anim2).with(anim4) 5 bouncer.play(anim5).after(amin2); 6 animatorSet.start();
3.5 TimeInterplator
Time interplator定义了属性值变化的方式,如线性均匀改变,开始慢然后逐渐快等。在Property Animation中是TimeInterplator,在View Animation中是Interplator,这两个是一样的。Interplator继承自TimeInterplator,内部没有任何其他代码。
- AccelerateInterpolator 加速,开始时慢中间加速
- DecelerateInterpolator 减速,开始时快然后减速
- AccelerateDecelerateInterolator 先加速后减速,开始结束时慢,中间加速
- AnticipateInterpolator 反向 ,先向相反方向改变一段再加速播放
- AnticipateOvershootInterpolator 反向加回弹,先向相反方向改变,再加速播放,会超出目的值然后缓慢移动至目的值
- BounceInterpolator 跳跃,快到目的值时值会跳跃,如目的值100,后面的值可能依次为85,77,70,80,90,100
- CycleIinterpolator 循环,动画循环一定次数,值的改变为一正弦函数:Math.sin(2 * mCycles * Math.PI * input)
- LinearInterpolator 线性,线性均匀改变
- OvershottInterpolator 回弹,最后超出目的值然后缓慢改变到目的值
- TimeInterpolator 一个接口,允许你自定义interpolator,以上几个都是实现了这个接口
3.6综合示例
1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.apis.animation; 18 19 // Need the following import to get access to the app resources, since this 20 // class is in a sub-package. 21 import android.animation.Animator; 22 import android.animation.ObjectAnimator; 23 import android.widget.LinearLayout; 24 import com.example.android.apis.R; 25 26 import android.animation.AnimatorListenerAdapter; 27 import android.animation.Keyframe; 28 import android.animation.LayoutTransition; 29 import android.animation.PropertyValuesHolder; 30 import android.view.View; 31 import android.view.ViewGroup; 32 import android.widget.CheckBox; 33 import android.widget.CompoundButton; 34 35 import android.app.Activity; 36 import android.os.Bundle; 37 import android.widget.Button; 38 39 /** 40 * This application demonstrates how to use LayoutTransition to automate transition animations 41 * as items are hidden or shown in a container. 42 */ 43 public class LayoutAnimationsHideShow extends Activity { 44 45 private int numButtons = 1; 46 ViewGroup container = null; 47 private LayoutTransition mTransitioner;//定义ViewGroup组件变化时的动画 48 49 /** Called when the activity is first created. */ 50 @Override 51 public void onCreate(Bundle savedInstanceState) { 52 super.onCreate(savedInstanceState); 53 setContentView(R.layout.layout_animations_hideshow); 54 55 final CheckBox hideGoneCB = (CheckBox) findViewById(R.id.hideGoneCB); 56 57 container = new LinearLayout(this); 58 container.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 59 ViewGroup.LayoutParams.MATCH_PARENT)); 60 61 // Add a slew of buttons to the container. We won't add any more buttons at runtime, but 62 // will just show/hide the buttons we've already created 63 for (int i = 0; i < 4; ++i) { 64 Button newButton = new Button(this); 65 newButton.setText(String.valueOf(i)); 66 container.addView(newButton); 67 newButton.setOnClickListener(new View.OnClickListener() { 68 public void onClick(View v) { 69 v.setVisibility(hideGoneCB.isChecked() ? View.GONE : View.INVISIBLE); 70 } 71 }); 72 } 73 74 resetTransition(); //使用默认的LayoutTransition 75 76 ViewGroup parent = (ViewGroup) findViewById(R.id.parent); 77 parent.addView(container); //将4个button对应的ViewGroup添加到主布局中 78 79 Button addButton = (Button) findViewById(R.id.addNewButton); 80 addButton.setOnClickListener(new View.OnClickListener() { 81 public void onClick(View v) { 82 for (int i = 0; i < container.getChildCount(); ++i) { 83 View view = (View) container.getChildAt(i); 84 view.setVisibility(View.VISIBLE);//显示4个button 85 } 86 } 87 }); 88 89 CheckBox customAnimCB = (CheckBox) findViewById(R.id.customAnimCB); 90 customAnimCB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 91 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 92 long duration; 93 if (isChecked) { //使用自定义LayoutTransition 94 mTransitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 30); 95 mTransitioner.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 30); 96 setupCustomAnimations(); 97 duration = 500; 98 } else { //使用默认LayoutTransition 99 resetTransition(); 100 duration = 300; 101 } 102 mTransitioner.setDuration(duration); 103 } 104 }); 105 } 106 107 private void resetTransition() { 108 mTransitioner = new LayoutTransition(); 109 container.setLayoutTransition(mTransitioner); 110 } 111 112 private void setupCustomAnimations() { 113 // Changing while Adding 114 PropertyValuesHolder pvhLeft = 115 PropertyValuesHolder.ofInt("left", 0, 1); 116 PropertyValuesHolder pvhTop = 117 PropertyValuesHolder.ofInt("top", 0, 1); 118 PropertyValuesHolder pvhRight = 119 PropertyValuesHolder.ofInt("right", 0, 1); 120 PropertyValuesHolder pvhBottom = 121 PropertyValuesHolder.ofInt("bottom", 0, 1); 122 PropertyValuesHolder pvhScaleX = 123 PropertyValuesHolder.ofFloat("scaleX", 1f, 0f, 1f); 124 PropertyValuesHolder pvhScaleY = 125 PropertyValuesHolder.ofFloat("scaleY", 1f, 0f, 1f); 126 final ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder( 127 this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScaleX, pvhScaleY). 128 setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_APPEARING)); 129 mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);//设置组件CHANGE_APPEARING动画 130 changeIn.addListener(new AnimatorListenerAdapter() { 131 public void onAnimationEnd(Animator anim) { 132 View view = (View) ((ObjectAnimator) anim).getTarget(); 133 view.setScaleX(1f); 134 view.setScaleY(1f); 135 } 136 }); 137 138 // Changing while Removing 139 Keyframe kf0 = Keyframe.ofFloat(0f, 0f); 140 Keyframe kf1 = Keyframe.ofFloat(.9999f, 360f); 141 Keyframe kf2 = Keyframe.ofFloat(1f, 0f); 142 PropertyValuesHolder pvhRotation = 143 PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2); 144 final ObjectAnimator changeOut = ObjectAnimator.ofPropertyValuesHolder( 145 this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhRotation). 146 setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_DISAPPEARING));//设置组件CHANGE_DISAPPEARING动画 147 mTransitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeOut); 148 changeOut.addListener(new AnimatorListenerAdapter() { 149 public void onAnimationEnd(Animator anim) { 150 View view = (View) ((ObjectAnimator) anim).getTarget(); 151 view.setRotation(0f); 152 } 153 }); 154 155 // Adding 156 ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 90f, 0f). 157 setDuration(mTransitioner.getDuration(LayoutTransition.APPEARING)); 158 mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);//设置组件APPEARING动画 159 animIn.addListener(new AnimatorListenerAdapter() { 160 public void onAnimationEnd(Animator anim) { 161 View view = (View) ((ObjectAnimator) anim).getTarget(); 162 view.setRotationY(0f); 163 } 164 }); 165 166 // Removing 167 ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotationX", 0f, 90f). 168 setDuration(mTransitioner.getDuration(LayoutTransition.DISAPPEARING)); 169 mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut);//设置组件DISAPPEARING动画 170 animOut.addListener(new AnimatorListenerAdapter() { 171 public void onAnimationEnd(Animator anim) { 172 View view = (View) ((ObjectAnimator) anim).getTarget(); 173 view.setRotationX(0f); 174 } 175 }); 176 177 } 178 }
这个是官方apiDemo源码,解释下其中代码:
Property animation系统还提供了对ViewGroup中的View改变加入动画的功能。
可以使用 LayoutTransition 对ViewGroup中的View改变进行动画显示。
上文的动画效果都是设置给容器(ViewGroup),然而效果是通过容器存放的View来体现的。
四种容器转换动画类型
当你添加或者移除ViewGroup中的View时,或者你调用View的setVisibility()方法来控制其显示或消失时,就处于一个转换状态。这种事件就有可能会激发动画。当前被增加或者移除的View可以经历一个出现的动画或者一个消失的动画。而且不止是当前要控制的View,ViewGroup中的其他View也可以随之进行变动,比如经历一个动画移动到新的位置。
一共有四种相关的动画类型:
1.View本身的出现动画;
2.消失动画;
3.由于新增了其他View而需要改变位置的动画;
4.由于移除了其他View而需要改变位置的动画。
(如果增加或移除了其他View之后,当前View的位置不需要改变,则无动画)。
你可以自定义这些动画,通过setAnimator() 方法把它们设置进一个 LayoutTransition 对象中去。
设置的时候需要一个 Animator 对象和一个常数:
APPEARING - 对出现的view设置
CHANGE_APPEARING - 当出现的view时,对其他受影响view设置
DISAPPEARING - 对消失的view设置
CHANGE_DISAPPEARING - 当消失view时,对其他受影响view设置
你可以自己定义这四种事件类型的动画,也可以使用默认的动画。最后通过setLayoutTransition(LayoutTransition)方法把这些动画以一个 LayoutTransition 对象的形式设置给一个ViewGroup即可。