概述 动画主要分为三种:View动画、帧动画、属性动画,分别介绍:
View动画 View动画的作用对象是View,主要包含平移动画
、缩放动画
、旋转动画
、透明度动画
。分别对应TranslateAnimation
、ScaleAnimation
、RotateAnimation
、AlphaAnimation
。可以通过XML来实现也可以通过代码,需要注意一点的是View动画最大的缺点是不能交互,View自身的属性并不会发生变化。例如对一个放大的View进行点击,点击区域其实还是原本View的点击区域。
Animation自身的属性 上面提到的属性都是Animation子类特有的属性,Animation自身带有:
duration 设置动画持续时间,单位为毫秒
fillAfter 如果为true则在动画结束后保持动画结束时的状态
fillBefore 如果设置为true则在动画结束后还原到动画前的状态
fillEnabled 和fillBefore效果相同
repeatCount 动画重复次数
repeatMode 重复类型,包括reverse和restart两个值,分别是倒序播放和重新播放,必须与repeatCount一起才会有效果,因为这里的意义是回放时的动作
interpolator 设置插值器,该属性影响动画的速度,可以不指定,默认为加速减速差值器(AccelerateDecelerateInterpolator)
TranslateAnimation 在Xml中定义,fromXDelta、toYDelta等都是相对当前View的位置。1
2
3
4
5
6
<translate xmlns:android ="http://schemas.android.com/apk/res/android"
android:fromXDelta ="0"
android:fromYDelta ="0"
android:toXDelta ="500"
android:toYDelta ="500"
/>
1
2
3
4
Animation translateAnimation = AnimationUtils.loadAnimation(MainActivity.this , R.anim.translate);
translateAnimation.setDuration(5000 );
view.startAnimation(translateAnimation);
ScaleAnimation 主要有6个属性如下:分别是:起始X轴上的相对自身比例的缩放,比如1是不变2是放大一倍;结束的X方向上相对自身的缩放比例;之后Y轴的类似。pivotX
是缩放轴坐标的X坐标,可以是数值、百分数、百分数p 三种样式,比如 50、50%、50%p,当为数值时,表示在当前View的左上角,即原点处加上50px,做为起始缩放点;如果是50%,表示在当前控件的左上角加上自己宽度的50%做为起始点;如果是50%p,那么就是表示在当前的左上角加上父控件宽度的50%做为起始点x轴坐标;pivotY
类似
1
2
3
4
5
6
7
8
9
<scale android:fromXScale ="0"
android:toXScale ="1.4"
android:fromYScale ="0"
android:toYScale ="1.4"
android:pivotX ="0"
android:pivotY ="0"
android:repeatCount ="2"
android:duration ="5000"
xmlns:android ="http://schemas.android.com/apk/res/android" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Animation scaleAnimation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.scale);
Animation scaleAnimation2 = new ScaleAnimation(0f,1.4f,0f,1.4f,0f,0f);
view.startAnimation(scaleAnimation);
```
##### RotateAnimation
主要有四个属性,如下,分别是:起始的角度,结束的角度,和旋转中心点的坐标,也是对于View的相对坐标
``` xml
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="0"
android:pivotY="0"
/>
1
2
3
4
Animation rotateAnimation = AnimationUtils.loadAnimation(MainActivity.this , R.anim.rotate);
rotateAnimation.setDuration(5000 );
view.startAnimation(rotateAnimation);
AlphaAnimation 透明动画相对简单一点,主要有fromAlpha和toAlpha,分别为是起始透明度和结束透明度,值是从0.0到1.0,表示全透明到不透明。1
2
3
4
5
6
7
8
9
10
<alpha xmlns:android ="http://schemas.android.com/apk/res/android"
android:fromAlpha ="0"
android:toAlpha ="1"
android:duration ="5000"
/>
Animation alphaAnimation = new AlphaAnimation(0f,1f);
alphaAnimation.setDuration(5000);
Animation alphaAnimation2 = AnimationUtils.loadAnimation(MainActivity.this, R.anim.alpha);
view.startAnimation(alphaAnimation);
set 动画合集 set中包含shareInterpolator,表示set中国的动画是否共享一个差值器。同时它还有继承于Animation的属性,如果在set标签下设置了某个属性则对标签内的所有Animation起作用。
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
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android ="http://schemas.android.com/apk/res/android"
android:duration ="5000"
android:fillAfter ="true" >
<alpha
android:fromAlpha ="0"
android:toAlpha ="1" />
<rotate
android:fromDegrees ="0"
android:pivotX ="0"
android:pivotY ="0"
android:toDegrees ="360" />
<scale
android:fromXScale ="0"
android:fromYScale ="0"
android:pivotX ="0"
android:pivotY ="0"
android:toXScale ="1.4"
android:toYScale ="1.4" />
<translate
android:fromXDelta ="0"
android:fromYDelta ="0"
android:toXDelta ="500"
android:toYDelta ="500" />
</set >
```
``` java
Animation scaleAnimation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.set);
view.startAnimation(scaleAnimation);
帧动画 帧动画就是按照顺序播放一些列的图片,实际上是AnimationDrawable。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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/takeout_img_loading_pic1" android:duration ="84" />
<item android:drawable ="@drawable/takeout_img_loading_pic2" android:duration ="167" />
<item android:drawable ="@drawable/takeout_img_loading_pic3" android:duration ="125" />
<item android:drawable ="@drawable/takeout_img_loading_pic4" android:duration ="42" />
</animation-list >
``` xml
<View
android:id ="@+id/image_view"
android:layout_width ="50dp"
android:background ="@drawable/frame"
android:layout_height ="50dp" />
可以将其设置为一个View的background,然后((AnimationDrawable)findViewById(R.id.image_view).getBackground()).start();
View动画的其他场景 View动画除了可以对单个View设置动画效果外也可以设置ViewGoup中子元素的加入效果,也可以设置Activity和Fragment的进入和退出效果
LayoutAnimation LayoutAnimation的属性包括delay
:设置动画开始的延迟,比如动画的duration为50ms,delay0.5意味着每个元素都要延迟25ms后进入,第一个元素25ms后进入,第二个元素50ms,以此类推。AnimationOrder
是动画的顺序,包括normal顺序进入,random随机,reverse反序.Animation
是指定的动画。
需要注意的是LayoutAnimation只在第一次创建的时候才有效,之后向adapter里添加时时无效的。1
2
3
4
5
6
7
8
9
10
11
<layoutAnimation xmlns:android ="http://schemas.android.com/apk/res/android"
android:animation ="@anim/set"
android:animationOrder ="normal"
android:delay ="0.5" >
</layoutAnimation >
<ListView
android:id ="@+id/listview"
android:layout_width ="wrap_content"
android:layout_height ="wrap_content"
android:layoutAnimation ="@anim/layout" ></ListView >
LayoutAnimation的java代码实现需要借助LayoutAnimationController
1
2
3
4
5
Animation animation = AnimationUtils.loadAnimation(MainActivity.this , R.anim.set);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f );
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
listView.setLayoutAnimation(controller);
同样的GridView也有系统提供的Animation,具体实现为GridLayoutAnimation,不详细介绍。
Activity的Fragemnt的过渡动画 可以通过overridePendingTransition(int enterAnim, int exitAnim)()
来为Activity添加进入和退出的动画,第一个参数是第二个Activity的进入动画,第二个参数是第一个Activity的退出动画,需要注意的是:overridePendingTransition(int enterAnim, int exitAnim)()
这个方法必须在startActivity
之后调用或者在finish()
之后调用1
2
3
4
5
6
Intent intent = new Intent(MainActivity.this , Main2Activity.class);
startActivity(intent);
overridePendingTransition(R.anim.activity_in,R.anim.activty_out);
finish();
overridePendingTransition(android.R.anim.fade_in,android.R.anim.fade_out);
Interpolator 插值器 插值器(Interpolator)是Animation的一个属性,用来设置动画变化过程的,Android自身提供了几种插值器:
AccelerateDecelerateInterpolator 在动画开始与介绍的地方速率改变比较慢,在中间的时候加速
AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速
AnticipateInterpolator 开始的时候向后然后向前甩
AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值
BounceInterpolator 动画结束的时候弹起
CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
DecelerateInterpolator 在动画开始的地方快然后慢
LinearInterpolator 以常量速率改变
OvershootInterpolator 向前甩一定值后再回到原来位置
在XML中如下设置:1
2
3
4
5
6
<rotate xmlns:android ="http://schemas.android.com/apk/res/android"
android:toDegrees ="360"
android:interpolator ="@android:anim/accelerate_decelerate_interpolator"
android:pivotX ="0"
android:pivotY ="0"
android:fromDegrees ="0" />
Property Animator 属性动画 属性动画是api11之后引入的,可以实现View动画不实现的功能,比如实现颜色的变化。View动画的作用对象是View,而属性动画可以针对的是对象的属性,属性动画可以通过改变对象的属性来实现动画效果,而View动画是通过其parent view实现的,在View被draw通过改变它的绘制参数,这样虽然View的大小或者旋转改变了,但是View的实际属性并没有变。
属性动画包括:ValueAnimator和ObjectAnimator,其中ObjectAnimator是ValueAnimator的子类。
ValueAnimator ValueAnimator主要是对值进行变化的,不依赖于任何控件,通过对值进行变化然后添加相应的listener。 主要包含getAnimatedValue()
、setRepeatCount()
、setRepeatMode()
、cancle()
其中setRepeatCount和setRepeatMode和View动画一样,只有在设置了repeatCount之后repeatMode才生效。cancle()则可以中断动画。AnimatorUpdateListener只在ValueAnimator类中有。1
2
3
4
5
6
7
8
9
10
11
animator = ValueAnimator.ofInt(0 , 500 );
animator.setDuration(3000 );
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.setRepeatCount(1 );
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate (ValueAnimator animation) {
int curValue = (int )animation.getAnimatedValue();
view.layout(curValue, curValue, curValue + view.getWidth(), curValue + view.getHeight());
}
});
同时Animator还可以设置AnimatorLIstener
,主要包括: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
public static interface AnimatorListener {
* <p>Notifies the start of the animation.</p>
*
* @param animation The started animation.
*/
void onAnimationStart (Animator animation) ;
* <p>Notifies the end of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* @param animation The animation which reached its end.
*/
void onAnimationEnd (Animator animation) ;
* <p>Notifies the cancellation of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* @param animation The animation which was canceled.
*/
void onAnimationCancel (Animator animation) ;
* <p>Notifies the repetition of the animation.</p>
*
* @param animation The animation which was repeated.
*/
void onAnimationRepeat (Animator animation) ;
}
同时对AnimatorUpdateListener提供了animator.removeAllUpdateListeners();
和animator.removeUpdateListener(AnimatorUpdateListener listener)
对AnimatorListener也提供了相应的方法RemoveAllListener()和RemoveListener(AnimatorListener listener),当调用RemoveAllListener时也会对PauseListener进行remove操作。
Animator 的其他方法:pause()、resume()和isPause()、isRunning()、setStartDelay()、clone()
自定义插值器(Interpolator) 在Aniamator中也存在插值器,同样是用于设置动画效果的,例如设置运动速率,设置轨迹等。在前面介绍了几种系统内置的插值器,下面自定义自己的插值器:
看下系统的插值器LinearInterpolator
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class LinearInterpolator implements Interpolator {
public LinearInterpolator () {
}
public LinearInterpolator (Context context, AttributeSet attrs) {
}
public float getInterpolation (float input) {
return input;
}
}
public interface Interpolator extends TimeInterpolator {
}
它实现了Interpolator
接口,而Interpolator
没添加其他接口,只是继承了TimeInterpolator
,看下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
* A time interpolator defines the rate of change of an animation. This allows animations
* to have non-linear motion, such as acceleration and deceleration.
*/
public interface TimeInterpolator {
* Maps a value representing the elapsed fraction of an animation to a value that represents
* the interpolated fraction. This interpolated value is then multiplied by the change in
* value of an animation to derive the animated value at the current elapsed animation time.
*
* @param input A value between 0 and 1.0 indicating our current point
* in the animation where 0 represents the start and 1.0 represents
* the end
* @return The interpolation value. This value can be more than 1.0 for
* interpolators which overshoot their targets, or less than 0 for
* interpolators that undershoot their targets.
*/
float getInterpolation (float input) ;
}
里面存在一个getInterpolation(float input),这个方法的参数是动画的进度,从0到1,是匀速的不受其他条件变化,只随时间进行进度的变化,输出则是动画的进度值。 例如下面的:1
2
3
4
5
6
7
8
9
10
ValueAnimator anim = ValueAnimator.ofInt(100 , 400 );
anim.setDuration(1000 );
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate (ValueAnimator animation) {
float currentValue = (float ) animation.getAnimatedValue();
Log.d("TAG" , "cuurent value is " + currentValue);
}
});
anim.start();
getAnimatedValue
的值就是 100 + (400 - 100)进度值,进度值 = 动画已持续时间 / 动画总时间 自定义的数值。1
2
3
4
5
6
7
public class CustomInterpolator implements Interpolator {
@Override
public float getInterpolation (float input) {
double x = input * Math.PI / 2 ;
return (float ) Math.sin(x);
}
}
上面实现了在动画时间内完成一个0-90度正弦效果的运动速率。
Evaluator 我们可以通过进度值来改变动画效果,也可以通过设置Evaluator,在ofInt()和OfFloat()时,系统会返回默认的IntEvaluator和FloatEvaluator1
2
3
4
5
6
public class IntEvaluator implements TypeEvaluator <Integer > {
public Integer evaluate (float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int )(startInt + fraction * (endValue - startInt));
}
}
可以看到这个就是上面提到的公式,最终我们getAnimatedValue
得到的值与Evaluator和Interpolator有关
自定义Evaluator
1
2
3
4
5
6
7
public class MyEvaluator implements TypeEvaluator <Integer > {
@Override
public Integer evaluate (float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int )(200 +startInt + fraction * (endValue - startInt));
}
}
上面的操作就在结果上多了200。我们可以调用Animator.setEvaluator()来设置
ArgbEvaluator 通过ArgbEvaluator可以设置颜色的渐变效果1
2
3
4
5
6
7
8
9
10
11
ValueAnimator animator = ValueAnimator.ofInt(0xffffff00 ,0xff0000ff );
animator.setEvaluator(new ArgbEvaluator());
animator.setDuration(3000 );
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate (ValueAnimator animation) {
int curValue = (int )animation.getAnimatedValue();
view.setBackgroundColor(curValue);
}
});
animator.start();
ArgbEvaluator的源码的原理和上面的原理相似,也是在startValue上加上当前进度 * (endValue - startValue)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ArgbEvaluator implements TypeEvaluator {
public Object evaluate (float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24 );
int startR = (startInt >> 16 ) & 0xff ;
int startG = (startInt >> 8 ) & 0xff ;
int startB = startInt & 0xff ;
int endInt = (Integer) endValue;
int endA = (endInt >> 24 );
int endR = (endInt >> 16 ) & 0xff ;
int endG = (endInt >> 8 ) & 0xff ;
int endB = endInt & 0xff ;
return (int )((startA + (int )(fraction * (endA - startA))) << 24 ) |
(int )((startR + (int )(fraction * (endR - startR))) << 16 ) |
(int )((startG + (int )(fraction * (endG - startG))) << 8 ) |
(int )((startB + (int )(fraction * (endB - startB))));
}
}
ObjectAnimator 我们可以看到ValueAnimator是通过改变数值,然后通过添加Listener,然后在回调中设置View的属性变化。为了简化整个流程,google推出了ObjectAnimator,其实ValueAnimator的子类,所以Valuemator有的方法,它都有。但它主要是通过反射获取到相应的属性的set方法,然后设置值。因此它的操作更加简单,如果参数或者属性错误则不会产生动画。
1
2
3
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotation" , 0 , 180 ,0 );
animator.setDuration(2000 );
animator.start();
View方法主要有以下几类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void setAlpha (float alpha)
public void setRotation (float rotation)
public void setRotationX (float rotationX)
public void setRotationY (float rotationY)
public void setTranslationX (float translationX)
public void setTranslationY (float translationY)
public void setScaleX (float scaleX)
public void setScaleY (float scaleY)
对比
因为原理是通过将设置的属性的第一个字母大写,然后添加set,找到该方法,然后设置值来产生动画。所以可以自定义一个属性来根据这个属性生成动画。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
public class CircleView extends View {
public CircleView (Context context) {
super (context);
}
public CircleView (Context context, AttributeSet attrs) {
super (context, attrs);
}
public CircleView (Context context, AttributeSet attrs, int defStyleAttr) {
super (context, attrs, defStyleAttr);
}
int r = 100 ;
@Override
protected void onDraw (Canvas canvas) {
super .onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(300 , 300 , r, paint);
}
public void setRadius (int radius) {
r = radius;
invalidate();
}
}
ObjectAnimator animator = ObjectAnimator.ofInt(view2, "radius" , 0 , 200 ,100 );
animator.setDuration(2000 );
animator.start();
效果如下