Android 动画大致可以分为view animation(视图动画,补间动画) ,drawable animation(帧动画),property animation(属性动画)
View Animation
视图动画只能作用于view对象,是对view的变换,有以下的四种变换
- AlphaAnimation(透明度变化)
- ScaleAnimation(缩放变化)
- TranslateAnimation(位移变化)
- RotateAnimation (旋转变化)
还有一个AnimationSet让上面的动画集合起来一起运行,都是Animation的子类
AlphaAnimation
直接alpha作为根元素
或者用set作为根元素
将xml加载到view中
alphaImageView.startAnimation(AnimationUtils.loadAnimation(MainActivity.this,R.anim.alpha));
再或者可以直接用java new 一个AlphaAnimation出来
AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f);
alphaAnimation.setDuration(1000);//必须设置一个大于0的数值,默认是0
alphaImageView.startAnimation(alphaAnimation);
运行的gif就不放了,用到的属性说明下,
- toAlpha 动画结束时的透明度 float (0.0 ~ 1.0,完全透明~完全不透明)
- fromAlpha 动画开始的透明度 float
- duration 动画持续时间 单位毫秒
- fillAfter 动画结束后是否保持结束后的状态 boolean true为保持
ScaleAnimation
参数介绍 :
- fromX 动画开始时X轴的缩放比例,1.0表示当前view的正常比例
- toX 动画结束时x轴的缩放比例,那么在x轴缩放的比例就是 toX - fromX
- fromY 类似
- toY 类似
- pivotX x方向上的缩放起点,默认是0
- pivotY Y方向上的缩放起点,默认是0
- pivotType 缩放起点的类型,有三种
/**
* The specified dimension is an absolute number of pixels.
* 默认是这种,相对于控件的0坐标
*/
public static final int ABSOLUTE = 0;
/**
* The specified dimension holds a float and should be multiplied by the
* height or width of the object being animated.
* 相对与自身的宽/高的百分比,1.0 == 100%
*/
public static final int RELATIVE_TO_SELF = 1;
/**
* The specified dimension holds a float and should be multiplied by the
* height or width of the parent of the object being animated.
* 相对于父控件的百分比
*/
public static final int RELATIVE_TO_PARENT = 2;
demo
ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 1.5f, 1.0f, 1.5f);
scaleAnimation.setDuration(2000);
scaleAnimation.setFillAfter(true);
alphaImageView.startAnimation(scaleAnimation);
把构造方法加上pivot,
ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 1.5f, 1.0f, 1.5f, 175f, 175f);
我们把缩放起点设置为175,那么它的缩放就从这个起点向两边开始缩放
ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 1.5f, 1.0f, 1.5f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
我们把它的起点模式改为relative_to_self,pivotX 和 pivotY改为0.5,意思就是它的缩放起点变为这个view的宽高的中点,向两边放大至原来的1.5倍
像上图实现图片点击放大,可以看到中间的红色框框,那就是imageView,其实它的真实位置并没有移动,这就是view animation,并不会真正改变view的位置,也不能。
对应的xml代码略...
TranslateAnimation
位移动画
* @param fromXType Specifies how fromXValue should be interpreted. One of
* Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
* Animation.RELATIVE_TO_PARENT.
fromXType 就是用于解释解释fromCValue,其余的也是同样意思,和上面的ScaleAnimation
TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 1.0f,
Animation.ABSOLUTE,0, Animation.ABSOLUTE, 0);
translateAnimation.setDuration(2000);
translateAnimation.setFillAfter(true);
alphaImageView.startAnimation(translateAnimation);
不放图了.....
RotateAnimation
RotateAnimation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(2000);
rotateAnimation.setFillAfter(true);
alphaImageView.startAnimation(rotateAnimation);
围绕图片中心转一圈,
AnimationSet
这里说一下50% 和 -50%p ,单纯只是%就是基于view本身,也就Animation.RELATIVE_TO_SELF,而%p就是基于view的parent,也就是Animation.RELATIVE_TO_PARENT.
图中的view宽度为60px,父view为100px,左右两边平分,各为20px,针对view的动画
这个的意思就是:
view动画的开始位置是:view在图中的位置向左移动距离(0.5 * 100 = 50 ,为什么是左,-50%p)px,就是view在图片当前的位置向左移动50px就开始动画,这个移动的过程当然是看不到的,并且这不是view真正的移动,这里只是文字表述,view其实一直不改变它所在的位置。
结束位置就是view当前所在的位置,也就是view的左边距离父的左边20px
Interpolator
通过定义Interpolator可以改变动画的速率,比如加速,减速,匀速等,Android定义了一系列的interpolator
accelerate decelerate interpolator
动画开始和动画结束的时候速度较慢,中间的速度较快。默认的插值器就是这个,在xml中的引用
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
accelerate interpolator
动画从开始到结束的过程中一直加速,有个参数factor,就是设置速率的变化的,factor默认是1.0,factor的值越大 ,开始的速度就会变得更加慢,结束的速度更加快。
第一张图片是默认的factor=1.0,第二张是factor=10.0
第二张的开始速度明显慢于第二张,结束速度就貌似也是挺快
anticipate interpolator
动画开始的时候先向后方抛,然后再猛地向前掷,看gif
第二张图是默认的accelerate_decelerate_interpolator,第二张是anticipate_interpolator
anticipate_interpolator 有个参数控制着抛得力度mTension,默认是2.0,力度越大,向后抛得越远
第一张是默认2.0,第二张是10,
anticipate overshoot interplator
开始的时候跟anticipate interpolator是一样的,只是结束的时候,会超过预定的位置之后然后再回到预定的位置
也是包含一个参数tension,拉力的力度
第一张默认的2.0,第二张自定义tension是10,tension影响着开始和结束的拉力
bounce interpolator
动画在结束的时候会跳动
cycle interpolator
动画做圆周运动,最终会回到初始的地方,
图片会做一个圆周运动,这个类中有一个参数mCycles控制这个周期运动的次数
decelerate interpolator
动画一开始的速度非常快,然后一直减速,有个控制参数factor,跟accelerate中的一样,是加速度,gif就不放了
linear interpolator####
动画匀速运动,没有设置的参数
overshoot interpolator
跟anticipate overshoot interpolator 差不多,就是没有开始的向后抛
https://github.com/lijinxiong/Animation
drawable animation
帧动画,也叫做视图动画,动画组成就是靠一张张的图片,快速的播放,达到动画的效果
xml
java
ImageView mImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = (ImageView) this.findViewById(R.id.my_image_view);
AnimationDrawable animationDrawable = (AnimationDrawable) mImageView.getBackground();
mImageView.setOnClickListener(new OnClilckListener(){
animationDrawable.start();
});
Java
mImageView = (ImageView) this.findViewById(R.id.my_image_view);
AnimationDrawable animationDrawable = new AnimationDrawable();
animationDrawable.addFrame(getResources().getDrawable(R.drawable._01), 100);
animationDrawable.addFrame(getResources().getDrawable(R.drawable._02), 100);
animationDrawable.addFrame(getResources().getDrawable(R.drawable._03), 100);
animationDrawable.addFrame(getResources().getDrawable(R.drawable._04), 100);
animationDrawable.addFrame(getResources().getDrawable(R.drawable._05), 100);
animationDrawable.setOneShot(false);
mImageView.setBackground(animationDrawable);
mImageView.setOnClickListener(new OnClilckListener(){
animationDrawable.start();
});
注意事项:
/**
* Starts the animation from the first frame, looping if necessary. This method has no effect
* if the animation is running.
*
* Note: Do not call this in the
* {@link android.app.Activity#onCreate} method of your activity, because
* the {@link AnimationDrawable} is not yet fully attached to the window.
* If you want to play the animation immediately without requiring
* interaction, then you might want to call it from the
* {@link android.app.Activity#onWindowFocusChanged} method in your
* activity, which will get called when Android brings your window into
* focus.
*
* @see #isRunning()
* @see #stop()
*/
就是说我们不能再activity的onCreate方法中调用start方法,因为这个时候的AnimationDrawable并没有完全的依附到了window上面去,如果你不需要用户交互的去触发这个动画,可以在onWindowFocusChanged中调用start方法
https://github.com/lijinxiong/FrameAnimation
Property Animation
Property Animation是Android3.0的时候引进的,可以对任意的对象的属性进行动画变换。你设定一个开始值和一个结束值,然后设定从开始值到结束值之间值是怎么变化的,还有整个变化所需要的时间,然后就通过不断的setProperty(),来达到动画的效果
与view animation的差别:先说文件的位置,xml文件,view animation是在anim 目录下的,而property animation是在animator。view animation并没有改变view 的真正位置,而property animation是真正改变view的属性来达到动画效果,当然是真正改变view的位置的。
先介绍一些用到的某些类和变量
- duration 动画时间
- 动画的执行类
- TimeInterpolator 时间插值器
- fraction 一个0-1的分数,由Animator 根据 当前动画播放时间/duration ,再经过xxxInterpolator计算,不同的interpolator结果不一样
- TypeEvaluator 根据fraction的值计算出动画改变属性的属性值
ValueAnimator
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(2000);//设置动画时间,默认是300
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());//设置插值器,默认也是这个
valueAnimator.setFloatValues(0, 200);//设置开始值和结束值
//监听属性值的改变
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float tem = (float) animation.getAnimatedValue();
Log.d(TAG, "onAnimationUpdate: " + tem);
view.setTranslationY(tem);
}
});
//开始动画
valueAnimator.start();
04-25 20:37:43.635 2224-2224/com.demo.propertyanimation D/MainActivity: onAnimationUpdate: 192.38795
04-25 20:37:43.651 2224-2224/com.demo.propertyanimation D/MainActivity: onAnimationUpdate: 193.37679
04-25 20:37:43.668 2224-2224/com.demo.propertyanimation D/MainActivity: onAnimationUpdate: 194.24664
04-25 20:37:43.684 2224-2224/com.demo.propertyanimation D/MainActivity: onAnimationUpdate: 195.10565
04-25 20:37:43.701 2224-2224/com.demo.propertyanimation D/MainActivity: onAnimationUpdate: 195.89684
04-25 20:37:43.719 2224-2224/com.demo.propertyanimation D/MainActivity: onAnimationUpdate: 196.57901
04-25 20:37:43.734 2224-2224/com.demo.propertyanimation D/MainActivity: onAnimationUpdate: 197.23698
04-25 20:37:43.751 2224-2224/com.demo.propertyanimation D/MainActivity: onAnimationUpdate: 197.82564
04-25 20:37:43.768 2224-2224/com.demo.propertyanimation D/MainActivity: onAnimationUpdate: 198.31593
04-25 20:37:43.784 2224-2224/com.demo.propertyanimation D/MainActivity: onAnimationUpdate: 198.76883
那么这个属性值是怎么计算出来的
- ValueAnimation先根据当前已经播放的动画时间/我们设置的duration时间,得到fraction
- 将这个值传递给动画设置的interpolator,这里对应的是AccelerateDecelerateInterpolator,它再根据本身的意义设置fraction,假如这里是LinearInterpolator,那么这个fraction 将不变
- 将fraction传递给TypeEvaluate,这里是FloatEvaluate,根据fraction计算出当前的属性值
FloatEvaluate
public class FloatEvaluator implements TypeEvaluator {
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}
ObjectAnimator
作为ValueAnimator的子类,ObjectAnimator不同的是,它需要设定动画作用的目标,也就是说这个动画应用在哪个目标上
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "y", 0, 200);
animator.setDuration(2000);
animator.setInterpolator(new OvershootInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float tem = (float) animation.getAnimatedValue();
Log.d(TAG, "onAnimationUpdate: " + tem);
}
});
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animator.start();
}
- ofFloat(Object target, String propertyName, float... values)
静态方法,返回一个ObjectAnimator,ValueAnimator也有这些静态方法
第一个参数就是动画应用在哪个对象,第二个参数就是设置这个动画改变的属性,第三个可变参数,写一个就是属性的开始值,两个就是一个开始值一个结束值。这里改变的属性值必须有setXxx方法,因为是通过反射调用设置这个属性的,还有就是这这个方法中必须包含对这个view 的重绘的方法,view.postInvalidate(); / view.invalidate();否则你设置了这个属性也不能在界面上马上看到效果,也就没有动画可言。 - addUpdateListener 每次对应的TypeEvaluate计算完属性值都会回调这个方法
- addListener 动画的开始结束取消重复监听,可以使用AnimatorListenerAdapter代替,自己选择监听哪个
TypeEvaluate
自定义TypeEvaluate
实现抛物线,x方向是100px/s,y方向的加速度是200px/s*s
ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator() {
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
PointF pointF = new PointF();
//fuction = t /duration
pointF.x = 100 * 2 * fraction;
pointF.y = 0.5f * 200 * 2 * fraction * 2 * fraction;
return pointF;
}
}, new PointF(0, 0));
animator.setDuration(2000);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF p = (PointF) animation.getAnimatedValue();
view.setTranslationX(p.x);
view.setTranslationY(p.y);
}
});
animator.start();
xml实现
objectAnimator
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.value_animator);
animator.setTarget(view);
animator.start();
animatorSet
java
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.value_animator_set);
set.setTarget(view);
set.start();
set中的一个参数就是ordering,顺序可以选择together(同一时间所有动画一起开始),也可以选择sequentially(按set中的顺序顺序一个个开始)。
纯java animatorSet
AnimatorSet set= new AnimatorSet();
set.play(AnimatorInflater.loadAnimator(this, R.animator.value_animator)).
with(new ObjectAnimator().setDuration(2000).ofFloat(view, "x", 0, 200));
set.setTarget(view);
set.setDuration(2000);
set.start();
https://github.com/lijinxiong/note/blob/master/Android/%E5%8A%A8%E7%94%BB.md