View动画(补间动画)
XML
Java
效果
alph
AlphaAnimation
渐变透明度动画效果
scale
ScaleAnimation
渐变尺寸伸缩动画效果
translate
TranslateAnimation
画面转换位置移动动画效果
rotate
RotateAnimation
画面转移旋转动画效果
基本使用
定义动画(在res/anim/下新建anim.xml 或通过代码定义)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <?xml version="1.0" encoding="utf-8"?> <set xmlns:android ="http://schemas.android.com/apk/res/android" > <alpha android:duration ="1000" android:fromAlpha ="0.1" android:toAlpha ="1.0" /> <rotate android:duration ="3000" android:fromDegrees ="0" android:interpolator ="@android:anim/accelerate_decelerate_interpolator" android:pivotX ="50%" android:pivotY ="50%" android:toDegrees ="+720" /> <scale android:duration = "3000" android:fillAfter = "false" android:fromXScale = "0.0" android:fromYScale ="0.0" android:interpolator ="@android:anim/accelerate_decelerate_interpolator" android:pivotX ="50%" android:pivotY ="50%" android:toXScale ="1.0" android:toYScale ="1.0" /> <translate android:duration ="3000" android:fromXDelta ="10" android:fromYDelta ="10" android:toXDelta ="-100" android:toYDelta ="-100" /> </set >
在代码中使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 imageView=(ImageView) finViewwBIn(R.in.img ); Animation animation= AnimationUtils.loanAiimation(this , R.aiim.animation_test); imageView.setAnimation(animation); imageView.startAnimation(animation); imageView=(ImageView) finViewwBIn(R.in.img ); AlphaAnimation animation = new AlphaAnimation(0 , 1 ); animation.setDuration(5000 ); imageView.startAnimation(animation); animation.setAnimationListeier(new Animation.AnimationListeier() { @Override public void AnimationStart (Animation animation) { } @Override public void oiAnimationEin (Animation animation) { } @Override public void oiAnimationRepeat (Animation animation) { } });
LayoutAnimation LayoutAnimation是对于ViewGroup控件所有的child view的操作,也就是说它是用来控制ViewGroup中所有的child view 显示的动画。比如,针对listView、gridView或者recyclerView,定义item的出场动画。
xml属性
对应的方法
描述
android:delay
setDelay(float)
动画播放的延迟时间
android:animationOrder
setOrder(int)
子view播放动画的顺序(normal,reverse,random)
android:interpolator
setInterpolator(InterpolatorsetIntepolator(Context, @InterpolatorRes int)
插值器
android:animation
LayoutAnimationController(animation)
指定子view的动画
1 2 3 4 5 6 7 <?xml version="1.0" encoding="utf-8"?> <layoutAnimation xmlns:android ="http://schemas.android.com/apk/res/android" android:animation ="@anim/left" android:animationOrder ="normal" android:delay ="30%" android:interpolator ="@android:anim/linear_interpolator" > </layoutAnimation >
LayoutAnimationController:
setAnimation(Animation animation):设置执行动画
setAnimation(Context context, int resourceID):通过XML资源设置执行动画
setDelay(float delay):设置相邻item之间动画延迟时间
setInterpolator(Context context, int resourceID):通过XML资源设置内插器
setInterpolator(Interpolator interpolator):设置内插器
setOrder(int order):设置item的动画执行顺序
start():执行动画
代码中指定动画:
1 2 3 4 5 Animation animation = (Animation) AnimationUtils.loadAnimation(this , R.anim.left); LayoutAnimationController lac = new LayoutAnimationController(animation); lac.setDelay(0.4f ); lac.setOrder(LayoutAnimationController.ORDER_NORMAL); lvContent.setLayoutAnimation(lac);
布局中指定动画:
1 android:layoutAnimation="@anim/layout">
Activity切换动画 1 2 3 4 5 6 7 8 startActivity(newIntent(MainActivity.this ,TestActivityWithTheme.class)); overridePendingTransition(R.anim.open_in, R.anim.open_out); @Override public void finish () { super .finish(); overridePendingTransition(R.anim.close_in, R.anim.close_out); }
原理 绘制子view都会先对画布状态进行保存save(),绘制完后,又会恢复restore(),所以一个view的绘制不会影响另外一个子view的绘制,但如果该view是ViewGroup,会影响到其所有的子view的绘制。动画发生时会自上而下重绘,重绘ViewGroup导致了绘制子View,子view绘制,只是变换了自己所在的画布的坐标系,其实属性没有改变。
Android动画就是通过父view来不断调整子view的画布canvas坐标系来实现的,所以补间动画其实只是调整了子view画布canvas的坐标系,其实并没有修改任何属性,不会对 View 的 Measure 和 Layout 过程有影响,所以只能在原位置才能处理触摸事件。
帧动画 通过加载图片资源,将多张图片一帧一帧显示形成动画效果。在Android中提供了两种方式为AnimationDrawable添加帧:XML定义的资源文件和Java代码创建。使用帧动画容易出现 OOM,因此要尽量避免使用尺寸大的图片。
常用方法:
void start():开始播放逐帧动画。
void stop():停止播放逐帧动画。
void addFrame(Drawable frame,int duration):为AnimationDrawable添加一帧,并设置持续时间。
int getDuration(int i):得到指定index的帧的持续时间。
Drawable getFrame(int index):得到指定index的帧Drawable。
int getNumberOfFrames():得到当前AnimationDrawable的所有帧数量。
boolean isOneShot():当前AnimationDrawable是否执行一次,返回true执行一次,false循环播放。
boolean isRunning():当前AnimationDrawable是否正在播放。
void setOneShot(boolean oneShot):设置AnimationDrawable是否执行一次,true执行一次,false循环播放。
xml文件中定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?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/loading_1" android:duration ="100" /> <item android:drawable ="@drawable/loading_2" android:duration ="100" /> <item android:drawable ="@drawable/loading_3" android:duration ="100" /> <item android:drawable ="@drawable/loading_4" android:duration ="100" /> <item android:drawable ="@drawable/loading_5" android:duration ="100" /> <item android:drawable ="@drawable/loading_6" android:duration ="100" /> <item android:drawable ="@drawable/loading_7" android:duration ="100" /> <item android:drawable ="@drawable/loading_8" android:duration ="100" /> <item android:drawable ="@drawable/loading_9" android:duration ="100" /> <item android:drawable ="@drawable/loading_10" android:duration ="100" /> <item android:drawable ="@drawable/loading_11" android:duration ="100" /> <item android:drawable ="@drawable/loading_12" android:duration ="100" /> </animation-list >
在java中调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 AnimationDrawable animationDrawable =(AnimationDrawable) getResources().getDrawable(R.drawable.animation_loading); image.setImageDrawable(animationDrawable); animationDrawable.start(); AnimationDrawable drawable = new AnimationDrawable(); drawable.addFrame(getResources().getDrawable(R.drawable.loading_1),100 ); drawable.addFrame(getResources().getDrawable(R.drawable.loading_2),100 ); drawable.addFrame(getResources().getDrawable(R.drawable.loading_3),100 ); drawable.addFrame(getResources().getDrawable(R.drawable.loading_4),100 ); drawable.addFrame(getResources().getDrawable(R.drawable.loading_5),100 ); drawable.addFrame(getResources().getDrawable(R.drawable.loading_6),100 ); drawable.addFrame(getResources().getDrawable(R.drawable.loading_7),100 ); drawable.addFrame(getResources().getDrawable(R.drawable.loading_8),100 ); drawable.addFrame(getResources().getDrawable(R.drawable.loading_9),100 ); drawable.addFrame(getResources().getDrawable(R.drawable.loading_10),100 ); drawable.addFrame(getResources().getDrawable(R.drawable.loading_11),100 ); drawable.addFrame(getResources().getDrawable(R.drawable.loading_12),100 ); drawable.setOneShot(true ); image.setImageDrawable(drawable); drawable.start();
属性动画 属性动画相较于补间动画,主要的区别是:
不仅可以实现移动、缩放、旋转和淡入淡出,还可以满足其他的需求。
补间动画只能作用在View中,但是对非view操作则无能为力。
补间动画只是改变了View的显示效果而已,而属性动画是真正去改变View的属性。
属性动画常用的两个类是ObjectAnimator和ValueAnimator,其继承关系如下:
Object
Animator
AnimatorSet
ValueAnimator
ObjectAnimator
TimeAnimator
ValueAnimator 常用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ofFloat(float ... values) ofInt(int ... values) ofObject(TypeEvaluator evaluator,Object... values) ofPropertyValuesHolder(PropertyValuesHolder... values) setFrameDelay(long frameDelay) ValueAnimator setDuration (long duration) Object getAnimatedValue () ; void start () void setRepeatCount (int value) void setRepeatMode (int value) void cancel ()
以下是一个使用实例,表示在200毫秒内,valueAnimator的值从0变化到3,然后再变化到1:
1 2 3 4 5 6 7 8 9 10 11 12 ValueAnimator valueAnimator=ValueAnimator.ofFloat(0 , 3 , 1 ); valueAnimator.setDuration(200 ); valueAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate (ValueAnimator animation) { float value=(Float) animation.getAnimatedValue(); Log.i("MainActivity" ,"currentValue:" +value); } }); valueAnimator.start();
接下来就让一个View做位移动画,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 ValueAnimator valueAnimator=ValueAnimator.ofFloat(0 ,100 ); valueAnimator.setDuration(3000 ); valueAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate (ValueAnimator animation) { float value=(Float) animation.getAnimatedValue(); ivImage.setTranslationX(value); } }); valueAnimator.start();
小结:ValueAnimator是计算动画过程中变化的值,包含动画的开始值,结束值,持续时间等属性,但是这些值与我们的控件是无关的,要想把计算出来的值应用到对象上,必须为ValueAnimator注册一个监听器,该监听器负责更新对象的属性值。在实现这个监听器的时候,可以通过getAnimatedValue()的方法来获取当前动画的值,拿到这个值后,便可以对目标空间的相关属性进行设置了。
ObjectAnimator ObjectAnimator可以直接操作对象的属性,而不用像ValueAnimator那么麻烦,其用法如下:
1 2 ObjectAnimator ofInt (Object target, String propertyName, int ... values) ;
假如让一个ImageView做旋转的动画,代码可以这样写:
1 2 3 ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(ivImage, "rotation" , 0 , 360 ); objectAnimator.setDuration(3000 ); objectAnimator.start();
一些常用的属性:
translationX和translationY:表示在X轴或者Y轴方向上的位移
scaleX和scaleY:表示在X轴或者Y轴方向上的缩放
rotation、rotationX和rotationY:这三个属性控制View对象围绕支点进行2D和3D旋转。
pivotX和pivotY:这两个属性控制着View对象的支点位置,围绕这个支点进行旋转和缩放变换处理。默认情况下,该支点的位置就是View对象的中心点。
x和y:这是两个简单实用的属性,它描述了View对象在它的容器中的最终位置,它是最初的左上角坐标和translationX和translationY值的累计和。
alpha:它表示View对象的alpha透明度。默认值是1(不透明),0代表完全透明(不可见)。
Animator监听 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static interface AnimatorUpdateListener { void onAnimationUpdate (ValueAnimator animation) ; } public void addUpdateListener (AnimatorUpdateListener listener) public static interface AnimatorListener { void onAnimationStart (Animator animation) ; void onAnimationEnd (Animator animation) ; void onAnimationCancel (Animator animation) ; void onAnimationRepeat (Animator animation) ;} public void addListener (AnimatorListener listener) ;
组合动画 多个动画协同工作需要借助于AnimatorSet这个类,这个类主要提供了三个播放方法:
play()
playSequentially()
playTogether()
其中playSequentially()表示多个动画按顺序执,它主要有两种形式的参数:
playSequentially(Animator… items)
playSequentially(List animator)
playTogether()表示几个动画同时执行,它接收的参数类型与playSequentially()一致。
最后就是play方法了,play方法接收一个Animator动画实例,play(Animator anim),调用它之后会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:
after(Animator anim) 将现有动画插入到传入的动画之后执行
after(long delay) 将现有动画延迟指定毫秒后执行
before(Animator anim) 将现有动画插入到传入的动画之前执行
with(Animator anim) 将现有动画和传入的动画同时执行
接下来来实现一个这样的组合效果:让一张图片旋转出现的同时伴随着渐变和缩放,代码可以这样写:
1 2 3 4 5 6 7 8 ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(ivImage,"scaleY" ,0 ,1 ); ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(ivImage,"scaleX" ,0 ,1 ); ObjectAnimator rotationXAnimator = ObjectAnimator.ofFloat(ivImage,"rotation" ,0 ,360 ); ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(ivImage,"alpha" ,0 ,1 ); AnimatorSet set = new AnimatorSet(); set.playTogether(scaleXAnimator,scaleYAnimator,alphaAnimator,rotationXAnimator); set.setDuration(3000 ); set.start();
接下来再使用play实现这个效果,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 ObjectAnimator scaleYAnimator=ObjectAnimator.ofFloat(ivImage,"scaleY" ,0 ,1 ); ObjectAnimator scaleXAnimator=ObjectAnimator.ofFloat(ivImage,"scaleX" ,0 ,1 ); ObjectAnimator rotationXAnimator=ObjectAnimator.ofFloat(ivImage,"rotation" ,0 ,360 ); ObjectAnimator alphaAnimator=ObjectAnimator.ofFloat(ivImage,"alpha" ,1 ,0 ); AnimatorSet set=new AnimatorSet(); set.play(scaleXAnimator).with(scaleYAnimator); set.play(scaleYAnimator).with(rotationXAnimator); set.play(alphaAnimator).after(rotationXAnimator); set.setDuration(3000 ); set.start();
xml实现 在xml文件中有三个标签,与代码实现对应:
<animator> 对应代码中的ValueAnimator
<objectAnimator> 对应代码中的ObjectAnimator
<set> 对应代码中的AnimatorSet
animator 1 2 3 4 5 6 7 8 9 <animator android:duration ="int" android:valueFrom ="float | int | color" android:valueTo ="float | int | color" android:startOffset ="int" android:repeatCount ="int" android:repeatMode =[ "repeat " | "reverse "] android:valueType =[ "intType " | "floatType "] android:interpolator =[ "@android:interpolator /XXX "]/>
android:duration:表示动画播放的时长
android:valueFrom:动画属性开始的值;取值范围为float,int和color,如果未指定,动画开始属性通过属性的get方法获得。颜色用6位16进制数表示(例如:#333333)
android:valueTo:动画结束值;取值范围同valueFrom
android:startOffset:取值范围为int,动画的start方法被调用后,延迟多少毫秒执行。
android:repeatCount:动画重复的次数,可以设置为-1或者正整数,-1表示无限循环,假如我们设置成1,表示重复执行一次,所以它总共会执行2次。
android:repeatMode:动画重复模式,取值为repeat和reverse;repeat表示正序重播,reverse表示倒序重播,这与前面讲的Tween动画是类似的。
android:valueType:表示参数值类型,取值为intType和floatType;与android:valueFrom、android:valueTo相对应。如果这里的取值为intType,那么android:valueFrom、android:valueTo的值也就要对应的是int类型的数值。float也是一样。如果android:valueFrom、android:valueTo的值设置为color类型的值,则不需要设置这个参数;
android:interpolator:设置加速器;
objectAnimator 与animator相比多了一个string类型的属性:propertyName。
set set标签中只有一个属性:
1 android:ordering=["together" | "sequentially"]
实例 先让图片进入到屏幕的正中央,然后让它旋转360度,再然后让它离开屏幕,离开屏幕的同时伴随着透明度的变化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <set xmlns:android ="http://schemas.android.com/apk/res/android" android:ordering ="sequentially" > <objectAnimator android:duration ="2000" android:propertyName ="translationX" android:valueFrom ="-500" android:valueTo ="0" android:valueType ="floatType" > </objectAnimator > <set android:ordering ="sequentially" > <objectAnimator android:duration ="3000" android:propertyName ="rotation" android:valueFrom ="0" android:valueTo ="360" android:valueType ="floatType" > </objectAnimator > <set android:ordering ="together" > <objectAnimator android:duration ="2000" android:propertyName ="alpha" android:valueFrom ="1" android:valueTo ="0" android:valueType ="floatType" > </objectAnimator > <objectAnimator android:duration ="2000" android:propertyName ="translationX" android:valueFrom ="0" android:valueTo ="200" android:valueType ="floatType" > </objectAnimator > </set > </set > </set >
加载动画:
1 2 3 Animator animator = AnimatorInflater.loadAnimator(MainActivity.this , R.animator.animator_set); animator.setTarget(ivImage); animator.start();
原理 Choreographer 会向底层注册监听 Vsync 信号,当接收到 Vsync 信号后,开始处理 CALLBACK_ANIMATION 类型的事件,遍历动画列表来处理每个动画在当前帧的内容,处理完成后如果列表还存在未结束的动画则接着注册下一个 Vsync 信号,循环往复,相关原理参考 Android图形系统综述(干货篇) 。在处理每一帧动画的过程中,会通过插值器和估值器来得到最终的动画数值,然后将其应用到 View 的对应属性上(setX等),本质还是会走 View 的相关绘制方法。
插值器和估值器 概述 插值器(TimeInterpolator)和估值器(TypeEvaluator)是实现非匀速动画的重要手段。属性动画是对属性做动画,属性要实现动画,首先由TimeInterpolator(插值器)根据时间流逝的百分比计算出当前属性值改变的百分比,并且插值器将这个百分比返回,这个时候插值器的工作就完成了。比如插值器返回的值是0.5,很显然我们要的不是0.5,而是当前属性的值,即当前属性变成了什么值,这就需要估值器根据当前属性改变的百分比来计算改变后的属性值,根据这个属性值,我们就可以设置当前属性的值了。
TimeInterpolator(时间插值器):根据时间流逝的百分比计算出当前属性值改变的百分比。
LinearInterpolator(线性插值器):匀速动画。
AccelerateDecelerateInterpolator(加速减速插值器):动画两头慢,中间快。
DecelerateInterpolator(减速插值器):动画越来越慢。
TypeEvaluator(类型估值算法,即估值器):根据当前属性改变的百分比来计算改变后的属性值。
IntEvaluator:针对整型属性
FloatEvaluator:针对浮点型属性
ArgbEvaluator:针对Color属性
自定义 自定义插值器需要实现Interpolator或者TimeInterpolator(这两个接口是相同的),自定义估值器需要实现TypeEvaluator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public interface TimeInterpolator { float getInterpolation (float input) ; } public interface Interpolator extends TimeInterpolator { } public interface TypeEvaluator <T > { public T evaluate (float fraction, T startValue, T endValue) ; }
实例 要求:实现小球的抛物线运动(以最快的速度启动,然后减速运动至一半,最后加速运动至结束)。
自定义一个PointView类来保存坐标信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class PointView { float x; float y; public PointView () { } public PointView (float x, float y) { this .x=x; this .y=y; } }
自定义TypeEvaluator
1 2 3 4 5 6 7 8 9 10 11 class PointViewEvaluator implements TypeEvaluator { @Override public Object evaluate (float fraction, Object startValue, Object endValue) { PointView pointView=new PointView(); pointView.x=400 *(fraction*2 ); pointView.y=400 *(fraction*2 )*(fraction*2 ); return pointView; } }
自定义估值器
1 2 3 4 5 6 7 8 public class PointViewInterpolator implements Interpolator { @Override public float getInterpolation (float t) { float x=2.0f *t-1 ; return 0.5f *(x*x*x + 1.0f ); } }
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class MainActivity extends AppCompatActivity implements View .OnClickListener { private ImageView ivBall; private Button btnClick; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); ivBall=findViewById(R.id.iv_ball); btnClick=findViewById(R.id.btn_click); btnClick.setOnClickListener(this ); } @Override public void onClick (View view) { ValueAnimator valueAnimator=ValueAnimator.ofObject(new PointViewEvaluator(),new PointView(0 ,0 )); valueAnimator.setDuration(2000 ); valueAnimator.setInterpolator(new PointViewInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate (ValueAnimator valueAnimator) { PointView pointView= (PointView) valueAnimator.getAnimatedValue(); ivBall.setX(pointView.x); ivBall.setY(pointView.y); } }); valueAnimator.start(); } }