当前Android应用开发涉及的动画主要有三种,分别是:视图动画,逐帧动画,属性动画。
逐帧动画
是在 xml 中定义好一系列图片之后,使用AnimationDrawable来顺序播放的动画。
位置:/res/drawable/frame_animation.xml
<?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/item" android:duration="100"/>
<item android:drawable="@drawable/item" android:duration="100"/>
<item android:drawable="@drawable/item" android:duration="100"/>
<item android:drawable="@drawable/item" android:duration="100"/>
</animation-list>
使用逐帧动画时,避免图片较多或图片较大,会引起OOM。
ImageView imageView = findViewById(R.id.image);
imageView .setBackgroundResource(R.drawable.rocket_thrust);
AnimationDrawable rocketAnimation =
(AnimationDrawable)imageView.getBackground();
rocketAnimation.start();//不可在onCreate中执行,要等view首次绘制完毕。
视图动画
针对View的影像进行缩放、透明度渐变、旋转、平移或组合使用,从而产生动画的效果。
Java类名 | xml关键字 | 描述 |
---|---|---|
AlphaAnimation | < alpha > | 渐进透明度动画效果 |
RotateAnimation | < rotate > | 旋转动画效果 |
ScaleAnimation | < scale > | 渐进尺寸伸缩动画效果 |
TranslateAnimation | < translate > | 平移动画效果 |
AnimationSet | < set > | 组合其他动画元素或set元素的容器 |
位置:res/anim/view_anim.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fillAfter="true"
android:shareInterpolator="true">
<rotate
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="360" />
<alpha
android:duration="200"
android:fillAfter="true"
android:fromAlpha="0"
android:interpolator="@android:anim/linear_interpolator"
android:repeatCount="-1"
android:repeatMode="reverse"
android:toAlpha="1" />
</set>
- duration:动画持续时间;
- fillAfter:动画结束时,是否保持最后一帧;
- repeatCount:动画循环的次数,默认0次不循环,-1为无线循环;
- repeatMode:动画循环模式,reverse指从动画结束处循环,restart指从动画开始处循环。
- interpolator:插值器,控制动画执行的速度。
- shareInterpolator:是否与set容器中其他动画元素共享插值器,false为各自使用自己的插值器。
- fromDegrees:旋转动画起始的角度,单位度,浮点值。
- pivotX:旋转中心的X坐标,n%表示相对于自身左边缘的 自身宽度的n%。
Animation animation= AnimationUtils.loadAnimation(this, R.anim.view_anim);
button.startAnimation(animation);
插值器
Interpolator是Animation类的一个xml属性,规定了从初始值过渡到结束值得渐变规律,视图动画中alpha、scale、rotate、translate、set动画元素都会继承此属性。
如下是系统内置的插值器实现:
插值器类名 | Resource ID | 描述 |
---|---|---|
AccelerateDecelerateInterpolator | @android:anim/accelerate_decelerate_interpolator | 系统默认插值器。在动画开始与结束时速度比较慢,在中间的时候加速。 |
AccelerateInterpolator | @android:anim/accelerate_interpolator | 在动画开始的地方速度比较慢,然后开始加速。 |
AnticipateInterpolator | @android:anim/anticipate_interpolator | 开始的时候向后然后向前甩。 |
AnticipateOvershootInterpolator | @android:anim/anticipate_overshoot_interpolator | 开始的时候向后然后向前甩一定值后返回最后的值。 |
BounceInterpolator | @android:anim/bounce_interpolator | 动画结束的时候弹起。 |
CycleInterpolator | @android:anim/cycle_interpolator | 动画循环播放特定的次数,速度沿着正弦曲线。 |
DecelerateInterpolator | @android:anim/decelerate_interpolator | 在动画开始的地方快然后慢。 |
LinearInterpolator | @android:anim/linear_interpolator | 常量速度。 |
OvershootInterpolator | @android:anim/overshoot_interpolator | 向前甩一定值后再回到原来位置。 |
PathInterpolator | @android:anim/path_interpolator | 新增,按照定义坐标路径动画。 |
属性动画
利用插值器和估值器,来计算出各个时刻 View 的属性,然后通过改变 View 的属性来实现 View 的动画效果。
- 时间插值器(Interpolator):作用是根据时间的流逝的百分比来计算属性改变的百分比。
- 类型估值器(TypeEvaluator):根据当前属性改变的百分比来计算改变后的属性值。
属性动画中的TimeInterpolator插值器兼容Interpolator接口,故视图插值器可在属性动画中使用。
ValueAnimator:通过从初始值到结束值得平滑过渡实现动画效果。addUpdateListener可监听动画执行过程。
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(5000);
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();
ObjectAnimator:ObjectAnimator继承ValueAnimator,可以对任意对象的属性方法进行操作,达到动画效果。
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f);
animator.setDuration(2000);
animator.start();
第一个参数表示动画目标对象,第二个参数表示对象属性名,后面的参数不固定,表示动画的初始和结束值。
组合动画:实际开发需求中,一般需要用到组合动画而不是单一动画。属性动画的组合借助AnimatotSet类实现。
- play(Animator anim):执行现有动画。
- after(Animator anim):将现有动画排在传入的动画之后执行。
- after(Animator anim):将现有动画延迟指定毫秒后执行。
- before(Animator anim):将现有动画排在传入的动画之前执行。
- with(Animator anim):将现有动画与传入的动画并行执行。
示例 1:先执行缩放动画,再并行执行旋转、平移、透明度动画。
ObjectAnimator scaleX = ObjectAnimator.ofFloat(textview, "scaleX", 1f, 0.5f, 1f);
ObjectAnimator alpha = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0.5f, 1f);
ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);
ObjectAnimator translationX = ObjectAnimator.ofFloat(textview, "translationX",
textview.getTranslationX(), textview.getTranslationX()-150f);
AnimatorSet animSet = new AnimatorSet();
animSet.play(rotate).with(translationX).with(alpha).after(scaleX);
animSet.setDuration(3000);
//监听动画状态
animSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
//动画结束
}
});
//监听动画进度
animSet.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
invalidate();
}
});
animSet.start();
translationX是View左上角相对父容器左上角在X轴的偏移量,translationX默认值为0。
自定义估值器
属性动画的自定义估值器需要实现TypeEvaluator接口,并复写evaluate()方法。
- 自定义路径估值器 PathAnimEvaluator
/**
* 路径估值器
*/
public class PathAnimEvaluator implements TypeEvaluator<PointModel> {
PointModel pointModel = null;
public PathAnimEvaluator(PointModel pointModel) {
this.pointModel = pointModel;
}
/**
* 动画执行算法
*
* @param fraction 表示动画完成度,属性改变的百分比
* @param startValue 动画初始值
* @param endValue 动画结束值
* @return 当前动画过渡值
*/
@Override
public PointModel evaluate(float fraction, PointModel startValue, PointModel endValue) {
//当前进度的x坐标 = 初始值+动画完成百分比*(结束值-初始值)。
float x = startValue.getX() + fraction * (endValue.getX() - startValue.getX());
float y = startValue.getY() + fraction * (endValue.getY() - startValue.getY());
Log.d("TestActvity", "fraction=" + fraction);
pointModel.setX(x);
pointModel.setY(y);
return pointModel;//避免造成非常多的内存碎片
}
}
从上述示例可知,对于任何实例对象,只要赋给他属性方法,并提供初始值和结束值,都可以实现从初始值到结束值的平滑过渡。
- 带动画的控件 AnimationView:从起点到终点,圆饼滑动。
public class AnimationView extends View {
private final float RADIUS = 100f;
//属性名
private PointModel pointModel;
private Paint paint;
public AnimationView(Context context) {
super(context);
init(context, null, 0);
}
public AnimationView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public AnimationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
paint = new Paint();
paint.setColor(Color.RED);
}
public PointModel getPointModel() {
Log.d("TestActvity", "getPointModel");
return pointModel;
}
public void setPointModel(PointModel pointModel) {
this.pointModel = pointModel;
invalidate();
Log.d("TestActvity", "setPointModel="+pointModel.getY());
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (pointModel == null) {
pointModel = new PointModel(RADIUS, RADIUS);
canvas.drawCircle(pointModel.getX(), pointModel.getY(), RADIUS, paint);
startAnimation();
} else {
canvas.drawCircle(pointModel.getX(), pointModel.getY(), RADIUS, paint);
}
}
private void startAnimation() {
//PointModel只是一个带x,y坐标的实体类
PointModel startPoint = new PointModel(RADIUS, RADIUS);
ObjectAnimator translate = ObjectAnimator.ofObject(this,
"pointModel",
new PathAnimEvaluator(startPoint),
startPoint, new PointModel(RADIUS, getHeight() - RADIUS));
translate.setDuration(10000);
//TODO 动画结束时弹跳效果未生效???
translate.setInterpolator(new BounceInterpolator());
translate.start();
}
}
动画在执行过程中会多次反射调用setPointModel()方法并赋新值,在这里需要添加触发UI刷新的操作,确保动画有效。
如果动画没有初始值,那么就会使用get方法提供的初始值,若还没有则会使用该类型的系统默认值或者报错。
自定义插值器
属性动画自定义插值器需实现TimeInterpolator接口,并复写getInterpolation()方法。
从上文自定义估值器可知,fraction表示动画作用对象的属性完成的百分比,其由系统调用插值器的getInterpolation()方法获得。
- 自定义速率插值器 PathAnimInterpolator
public class PathAnimInterpolator implements TimeInterpolator {
@Override
public float getInterpolation(float input) {
//动画开始时,input值 = 0;动画结束时input = 1
//实现先减速后加速的效果
float result;
if (input <= 0.5) {
result = (float) (Math.sin(Math.PI * input)) / 2;
} else {
result = (float) (2 - Math.sin(Math.PI * input)) / 2;
}
Log.d("TestActvity", "result=" + result);
return result;
}
}
如上在AnimationView中替换即可。
注意事项:
- 在activity销毁的时候,一定确保动画关闭,资源回收,避免内存泄露。
- 视图动画不能改变view的属性,只是对其影像做动画,需要view.clearAnimation()后方可正常操作其属性。
- 逐帧动画避免图片过大、过多,容易造成内存泄露。
- 开启硬件加速,可以提升动画的流畅性。
- 动画操作里,尽量用dp,而不是px,处理好屏幕适配问题。
网友评论