其实动画这个东西我已经了解过很长一段时间了,可是一直没系统的整理过。关于android中的各类动画虽然都会用,但总怕本身会慢慢遗忘。这回看了几篇动画分析的文章,本身也学到了一些东西,在此就梳理一下。html
参考博文以下,感谢大神们的分享:java
http://www.open-open.com/lib/view/open1329994048671.htmlandroid
http://www.tuicool.com/articles/yeM3my算法
http://blog.csdn.net/singwhatiwanna/article/details/17841165
编程
http://blog.csdn.net/lmj623565791/article/details/38067475api
http://blog.csdn.net/lmj623565791/article/details/38092093app
注意:全部view的动画就是被限制在它的父控件中的,即便你作了view的移动,它也不可能显示在父控件的外边。也就是说父控件是一个舞台,演员能够在舞台上处处走动,但若是超过了舞台,那么观众就看不到了。ide
1、View Animation(Tween Animation)函数
View Animation(Tween Animation):也可称为补间动画(Tween Animation),给出两个关键帧,经过一些算法将给定属性值在给定的时间内在两个关键帧间渐变。工具
View animation只能应用于View对象,并且只支持一部分属性,如支持缩放旋转而不支持背景颜色的改变。
对于View animation,它只是改变了View对象绘制的位置,注意这是“绘制”,而不是实际的位置。好比你让一个button变成它的两倍大小,可是它能接受你点击的区域仍是原来的button区域,没有作任何改变。
View Animation支持设定多种动画样式,也能够设定这些动画的执行顺序,支持经过xml和代码两种方式来设置动画效果。
动画举例
【 By XML 】
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" android:shareInterpolator="true" android:startOffset="50"> <alpha android:duration="200" android:fromAlpha="1.0" android:toAlpha="0.0" /> </set>
Animation aimation = AnimationUtils.loadAnimation(this, R.anim.anim); final ImageView imageView = (ImageView) findViewById(R.id.imageView_id); imageView.startAnimation(aimation);
能够对动画添加监听器
aimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { animation = null; } @Override public void onAnimationRepeat(Animation animation) { } });
若是你是用AnimationSet设置动画的话,animationSet也是继承自Animation,因此也有setAnimationListener的方法
详细代码能够参考:http://www.cnblogs.com/tianzhijiexian/p/3983616.html
【 By JAVA 】
经过java代码来作的,能够参考这篇文章。其实也就是各类类继承Animation,而后有本身独特的方法,也能够经过AnimationSet进行各类设置。
http://www.cnblogs.com/tianzhijiexian/p/3981241.html
Drawable Animation(Frame Animation):帧动画,就像GIF图片,经过一系列Drawable依次显示来模拟动画的效果。在XML中的定义方式以下:
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true">
<item android:drawable="@drawable/rocket_thrust1" android:duration="200" /> <item android:drawable="@drawable/rocket_thrust2" android:duration="200" /> <item android:drawable="@drawable/rocket_thrust3" android:duration="200" />
</animation-list>
必须以<animation-list>为根元素,以<item>表示要轮换显示的图片,duration属性表示各项显示的时间。XML文件要放在/res/drawable/目录下。
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.main); imageView = (ImageView) findViewById(R.id.imageView1); imageView.setBackgroundResource(R.drawable.drawable_anim); AnimationDrawable anim = (AnimationDrawable) imageView.getBackground(); } public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { anim.stop(); anim.start(); return true; } return super.onTouchEvent(event); }
我在实验中遇到两点问题:
3、Property Animation
属性动画,这个是在Android 3.0中才引进的,它更改的是对象的实际属性,如Button的缩放,Button的位置与大小属性值都改变了。并且Property Animation不止能够应用于View,还能够应用于任何对象(Object)。Property Animation只是表示一个值在一段时间内的改变,当值改变时要作什么事情彻底是你本身决定的。
在Property Animation中,能够对动画应用如下属性:
4、ValueAnimator
ValueAnimator包含Property Animation动画的全部核心功能,如动画时间,开始、结束属性值,相应时间属性值计算方法等。它其实就是一个计算器,并不能实际执行动画效果。它能够计算出动画要执行的时间,每隔几毫秒刷新一次等等,但具体如何执行动画,它是无论的。你须要在ValueAnimator的ValueAnimator.onUpdateListener监听器中进行设置。
这里给ValueAnimator对象设置了一个值,由于是一个值,因此会默认为是最终的结果值,动画默认从当前的值到你设定的这个值。若是你设定两个值,那么它就意味着是从第一个值到另外一个值进行动画。
// set one value,it is final value. // If you set two values in it.It means animation will start from first value to Second value. ValueAnimator animator = ValueAnimator.ofFloat(1f); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { } });
animator.start();
这个AnimationUpdateListener将会在动画执行过程当中触发。若是你没在这里作任何处理,那么即便是执行了(start())也不会有任何动画的。
实际运用——自由落体 & 抛物线
若是我但愿小球抛物线运动【实现抛物线的效果,水平方向100px/s,垂直方向加速度200px/s*s 】,分析一下,貌似只和时间有关系,可是根据时间的变化,横向和纵向的移动速率是不一样的,咱们该咋实现呢?此时就要重写TypeValue的时候了,由于咱们在时间变化的同时,须要返回给对象两个值,x当前位置,y当前位置。
/** * 抛物线 * @param view */ public void paowuxian(View view) { ValueAnimator valueAnimator = new ValueAnimator(); valueAnimator.setDuration(3000); valueAnimator.setObjectValues(new PointF(0, 0)); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.setEvaluator(new TypeEvaluator<PointF>() { // fraction = t / duration @Override public PointF evaluate(float fraction, PointF startValue, PointF endValue) { Log.e(TAG, fraction * 3 + ""); // x方向200px/s ,则y方向0.5 * 10 * t PointF point = new PointF(); point.x = 200 * fraction * 3; point.y = 0.5f * 200 * (fraction * 3) * (fraction * 3); return point; } }); valueAnimator.start(); valueAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { PointF point = (PointF) animation.getAnimatedValue(); mBlueBall.setX(point.x); mBlueBall.setY(point.y); } }); }
能够看到,由于ofInt,ofFloat等没法使用,咱们自定义了一个TypeValue,每次根据当前时间返回一个PointF对象,(PointF和Point的区别就是x,y的单位一个是float,一个是int;RectF,Rect也是)PointF中包含了x、y的当前位置,而后咱们在监听器中获取,动态设置属性。
5、ObjectAnimator
实际应用中通常都会用ObjectAnimator来产生某一对象的动画,但用ObjectAnimator有必定的限制,要想使用ObjectAnimator,应该知足如下条件:
若是上述条件不知足,则不能用ObjectAnimator,应用ValueAnimator代替。
下面是将imageview进行透明度渐变的例子。
ObjectAnimator oa=ObjectAnimator.ofFloat(imageview, "alpha", 0f, 1f); oa.setDuration(3000); oa.start();
根据应用动画的对象或属性的不一样,可能须要在onAnimationUpdate函数中调用invalidate()函数刷新视图。
属性动画要求动画做用的对象提供该属性的get和set方法,属性动画根据你传递的该熟悉的初始值和最终值,以动画的效果屡次去调用set方法,每次传递给set方法的值都不同,确切来讲是随着时间的推移,所传递的值愈来愈接近最终值。总结一下,你对object的属性xxx作动画,若是想让动画生效,要同时知足两个条件:
以上条件缺一不可
那么若是咱们的object没办法知足这个条件呢?好比button的setWidth方法仅仅是设置它的最小宽度(minWidth),对于实际宽度不会产生任何影响。若是让它实际改变就须要用
btn.getLayoutParams().width = xxx;
来设置,但这种经过方法来得到public值的方法又不适合ObjecAnimator的应用场景,改怎么办呢?
针对上述问题,Google告诉咱们有3中解决方法:
1. 给你的对象加上get和set方法,若是你有权限的话
对于View咱们没权限给其添加各类属性,因此这种方法仅仅适合于本身定义的Object对象
2. 用一个类来包装原始对象,间接为其提供get和set方法
这个方法很好,你能够写一个类继承那个Object对象,在子类中添加各类set、get方法,更好的办法是写一个包装类,传入一个object对象,而后给其添加各类方法。
private void performAnimate() { ViewWrapper wrapper = new ViewWrapper(mButton); ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start(); } @Override public void onClick(View v) { if (v == mButton) { performAnimate(); } } private static class ViewWrapper { private View mTarget; public ViewWrapper(View target) { mTarget = target; } public int getWidth() { return mTarget.getLayoutParams().width; } public void setWidth(int width) { mTarget.getLayoutParams().width = width; mTarget.requestLayout(); } }
上面类中ViewWrapper这个内部类就是一个包装类,它的构造函数传入一个View对象,而后提供了set和get方法,这样咱们就能够对它进行动画的操做了
3. 采用ValueAnimator,监听动画过程,本身实现属性的改变
这个方法就有些别扭了,但扩展性是最强的。ValueAnimator咱们在上面已经介绍过了,下面是在动画执行过程当中能的获得的东西。
ValueAnimator animator = ValueAnimator.ofFloat(1, 1); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Integer currentValue = (Integer)animation.getAnimatedValue(); // current Value(0~100) float fraction = valueAnimator.getAnimatedFraction(); // progress of animation } });
getAnimatedFraction() 这个能够获得当前动画的进度,获得的是浮点型数据颇有用。api12中加入的,感受比getAnimationValue有用
getAnimatedValue(String propertyName) 获得某个特定属性当前的值,这个可用性就很高了。若是有特殊要求能够用它
其他的属性都在下面了,看名字就知道是什么意思啦~
实际运用
这个例子是将一个view进行移动和拉伸的动画。涉及了多个属性,当你要让view选装或者是移动的时候,请务必设定PivotX,PivotY来指定坐标,若是不设定的话,移动和选择默认的中心点都是Object的中心
public void startViewSimpleAnim(final View fromView,Rect finalBounds,int startOffsetY,int finalOffsetY, float startAlpha, float finalAlpha) { Rect startBounds = new Rect(); startBounds.set(Position.getGlobalVisibleRect(fromView)); //设置偏移量 startBounds.offset(0, -startOffsetY); finalBounds.offset(0, -finalOffsetY); //设定拉伸或者旋转动画的中心位置,这里是相对于自身左上角 fromView.setPivotX(0f); fromView.setPivotY(0f); //计算拉伸比例 float scaleX = (float)finalBounds.width() / startBounds.width(); float scaleY = (float)finalBounds.height() / startBounds.height(); AnimatorSet set = new AnimatorSet(); ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(fromView, "alpha", startAlpha, finalAlpha); ObjectAnimator xAnim = ObjectAnimator.ofFloat(fromView, "x", startBounds.left, finalBounds.left); ObjectAnimator yAnim = ObjectAnimator.ofFloat(fromView, "y", startBounds.top, finalBounds.top); ObjectAnimator scaleXAnim = ObjectAnimator.ofFloat(fromView, View.SCALE_X, 1f, scaleX); ObjectAnimator scaleYAnim = ObjectAnimator.ofFloat(fromView, View.SCALE_Y,1f, scaleY); set.play(alphaAnim).with(xAnim).with(yAnim).with(scaleXAnim).with(scaleYAnim); set.setStartDelay(mStartDelay); set.setDuration(mAnimTime); set.setInterpolator(mInterpolator); set.addListener(new AnimListener(fromView,null)); set.start(); }
若是你操做对象的该属性方法里面,好比上例的setRotationX若是内部没有调用view的重绘,则你须要本身按照下面方式手动调用。
anim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // view.postInvalidate(); // view.invalidate(); } });
用xml文件来建立属性动画
你们确定都清楚,View Animator 、Drawable Animator均可以在anim文件夹下建立动画,而后在程序中使用,甚至在Theme中设置为属性值。固然了,属性动画其实也能够在文件中声明。
首先在res下创建animator文件夹,而后创建res/animator/scalex.xml
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:propertyName="scaleX" android:valueFrom="1.0" android:valueTo="2.0" android:valueType="floatType" > </objectAnimator>
而后经过动画工具类就能够加载xml中的动画文件了
AnimatorInflater.loadAnimator(this, R.anim.anim);
public void scaleX(View view) { // 加载动画 Animator anim = AnimatorInflater.loadAnimator(this, R.animator.scalex); anim.setTarget(mMv); anim.start(); }
纵向与横向同时缩放
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together" > <objectAnimator android:duration="1000" android:propertyName="scaleX" android:valueFrom="1" android:valueTo="0.5" > </objectAnimator> <objectAnimator android:duration="1000" android:propertyName="scaleY" android:valueFrom="1" android:valueTo="0.5" > </objectAnimator> </set>
使用set标签,有一个orderring属性设置为together,【还有另外一个值:sequentially(表示一个接一个执行)】
6、经过AnimatorSet来控制多个动画
AnimatorSet和AnimationSet相似,能够操做多个动画属性。AnimationSet提供了一个把多个动画组合成一个组合的机制,并可设置组中动画的时序关系,如同时播放,顺序播放等。
例子01:
AnimatorSet bouncer = new AnimatorSet(); bouncer.play(anim1).before(anim2); bouncer.play(anim2).with(anim3); bouncer.play(anim2).with(anim4) bouncer.play(anim5).after(amin2); animatorSet.start();
例子02:
animSet.playTogether(anim1, anim2);
animSet.start();
使用playTogether两个动画同时执行,固然还有playSequentially依次执行
例子03:
/** * anim1,anim2,anim3同时执行 * anim4接着执行 */ AnimatorSet animSet = new AnimatorSet(); animSet.play(anim1).with(anim2); animSet.play(anim2).with(anim3); animSet.play(anim4).after(anim3); animSet.setDuration(1000); animSet.start();
若是咱们有一堆动画,如何使用代码控制顺序,好比1,2同时;3在2后面;4在1以前
注意:animSet.play().with();也是支持链式编程的,可是不要想太多。
好比:animSet.play(anim1).with(anim2).before(anim3).before(anim5);
这样是不行的,系统不会根据你写的这一长串来决定前后的顺序,因此麻烦你按照上面例子的写法,多写几行
7、ViewPropertyAnimator
若是须要对一个View的多个属性进行动画能够用ViewPropertyAnimator类,该类对多属性动画进行了优化,会合并一些invalidate()来减小刷新视图,该类在3.1中引入。
例子01:
如下两段代码实现一样的效果
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f); ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
myView.animate().x(50f).y(100f);
例子02:
在SDK12的时候,给View添加了animate方法,更加方便的实现动画效果。
btn.animate().x(200).y(200).alpha(0f); 就能够实现动画效果了~
例子03:
简单的使用mBlueBall.animate().alpha(0).y(mScreenHeight / 2).setDuration(1000).start()就能实现动画~~不过须要SDK11,此后在SDK12,SDK16又分别添加了withStartAction和withEndAction用于在动画前,和动画后执行一些操做。固然也能够.setListener(listener)等操做。
package com.example.zhy_property_animation; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.app.Activity; import android.os.Bundle; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; import android.widget.ImageView; public class ViewAnimateActivity extends Activity { protected static final String TAG = "ViewAnimateActivity"; private ImageView mBlueBall; private float mScreenHeight; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.view_animator); DisplayMetrics outMetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(outMetrics); mScreenHeight = outMetrics.heightPixels; mBlueBall = (ImageView) findViewById(R.id.id_ball); } public void viewAnim(View view) { // need API12 mBlueBall.animate()// .alpha(0)// .y(mScreenHeight / 2).setDuration(1000) // need API 12 .withStartAction(new Runnable() { @Override public void run() { Log.e(TAG, "START"); } // need API 16 }).withEndAction(new Runnable() { @Override public void run() { Log.e(TAG, "END"); runOnUiThread(new Runnable() { @Override public void run() { mBlueBall.setY(0); mBlueBall.setAlpha(1.0f); } }); } }).start(); } }
8、TypeEvalutors<T>
根据属性的开始、结束值与TimeInterpolation计算出的因子计算出当前时间的属性值,Android提供了如下几个evalutor:
自定义TypeEvalutor很简单,只须要实现一个方法,如FloatEvalutor的定义:
public class FloatEvaluator implements TypeEvaluator { public Object evaluate(float fraction, Object startValue, Object endValue) { float startFloat = ((Number) startValue).floatValue(); return startFloat + fraction * (((Number) endValue).floatValue() - startFloat); } }
根据动画执行的时间跟应用的Interplator,会计算出一个0~1之间的因子,即evalute函数中的fraction参数,经过上述FloatEvaluator应该很好看出其意思。
9、当Layout改变时应用动画
ViewGroup中的子元素能够经过setVisibility使其Visible、Invisible或Gone,当有子元素可见性改变时,能够向其应用动画。
经过LayoutTransition类的常量(第一个参数)能够区分动画的类型,第二个参数为Animator。
mTransition.setAnimator(LayoutTransition.DISAPPEARING, customDisappearingAnim);
更多的解释
详细的例子,请参考:http://blog.csdn.net/lmj623565791/article/details/38092093
顺便说下:Android 3.0已经原生支持了Animating Layout,当Layout变化的时候,系统会根据Layout变化先后,自动显示动画。
只要给layout添加:android:animateLayoutChanges="true"便可,并且动画还能够经过setLayoutTransition()本身定义。
参考博文以下:
http://www.open-open.com/lib/view/open1329994048671.html
http://www.tuicool.com/articles/yeM3my
http://blog.csdn.net/singwhatiwanna/article/details/17841165