动画主要分为两种一种是Tween动画,一种 是属性动画。
Tween动画有分为帧动画和补间动画。
帧动画
作用对象: 只能是View,如ImageView Button等。
原理:把一张张图片连起来播放,视觉上看起来像动画一样。
特点: 使用简单,方便 但是使用图片过多导致包过大而且容易OOM。
具体使用:
首先在drawable目录新建一个frame_anim.xml 内容如下:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true">
<item android:drawable="@drawable/icon_logo" android:duration="1000"/>
<item android:drawable="@drawable/ic_launcher_background" android:duration="1000"/>
<item android:drawable="@drawable/ic_launcher" android:duration="1000"/>
</animation-list>
java代码如下
imageView.setBackgroundResource(R.drawable.frame_anim);
AnimationDrawable animation = (AnimationDrawable)imageView.getBackground();
animation.start();
补间动画
作用对象:和帧动画一样,只能针对View。
原理:通过确定开始视图的样子和结束视图的样子,中间的动画变化由系统帮我们补全。
分类:动画分为四类,位移(TranslateAnimation)动画、 缩放(ScaleAnimation)动画、透明(AlphaAnimation)动画、旋转(RotateAnimation)动画。
具体使用:可以在XML中定义,也可以在java代码中定义。
使用场景:现在常见的使用场景就是Activity fragment或者ViewGroup子类替换用(XML转场动画复用率比较高)。
属性动画
作用对象:任意java对象。
实现动画效果:可以实现任意的动画效果,并不一定只是定义的那四种。
原理:在一定的时间间隔内不断的是值进行改变,并且不断的将值赋值给对象的属性,从而实现该对象在改属性上的动画效果,这里可以是任意对象的任意属性。可以通过Interpolator来改变值变化的速率,通过TypeEvaluator来的操作。
实现属性动画主要有两个类一个是ValueAnimator和ObjectAnimator。
ValueAnimator
它的用法主要有三种
ValueAnimator.ofInt
设置开始值和结束值,然后设置addUpdateListener来监听值的变化从而来实现动画效果。
ValueAnimator.ofFloat
和ValueAnimator.ofInt类似,只是估值器不一样,ValueAnimator.ofFloat采用的是FloatEvaluator估值器;ValueAnimator.ofInt用的是IntEvaluator估值器。
public class FloatEvaluator implements TypeEvaluator<Number> {
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}
ValueAnimator.ofObject
相对与上面两个这个相对复杂一点,需要自己定义估值器。
public class Point {
private float x;
private float y;
public Point(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
}
public class MyView extends View {
public static final float RADIUS = 30f;
private Paint mPaint;
private float x;
private float y;
@Override
public float getX() {
return x;
}
@Override
public void setX(float x) {
this.x = x;
}
@Override
public float getY() {
return y;
}
@Override
public void setY(float y) {
this.y = y;
}
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, -1);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.YELLOW);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(x, y, RADIUS, mPaint);
}
}
public class PointEvaluator implements TypeEvaluator {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
Point startPoint = (Point) startValue;
Point endPoint = (Point) endValue;
float x = startPoint.getX() + (endPoint.getX() - startPoint.getX()) * fraction;
float y = startPoint.getY() + (endPoint.getY() - startPoint.getY()) * fraction;
return new Point(x,y);
}
}
private void animStart() {
final MyView myView = findViewById(R.id.myview);
Point start = new Point(30, 30);
Point end = new Point(500, 500);
ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(), start, end);
valueAnimator.setDuration(5000);
valueAnimator.setRepeatCount(100);
valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Point point = (Point) animation.getAnimatedValue();
myView.setX(point.getX());
myView.setY(point.getY());
myView.invalidate();
}
});
valueAnimator.start();
}
从上面的例子上看,实际还是对x和y的值进行模拟。需要自己定义TypeEvaluator,对值按照百分比进行估算。
ObjectAnimator
原理:直接对对象的属性值进行修改,从而实现动画效果,底层实现是基于ValueAnimator。
private void animStart1() {
//第一个值是执行动画的对象 第二个值是执行对象要改变的属性 第三和第四个值是初始值到结束值
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(btn,"translationX", 0, 1080);
//原理 首先估值器不断的改变值
//找到translationX 的set的方法不断的设置值
//调用requestLayout来改变值Button的布局
objectAnimator.setDuration(5000);
objectAnimator.start();
}
从上面的例子可以看出来,其实很容易的实现自定义属性主要有两种方式,一种可以定义属性,添加get和set方法,另外一种是通过一个包装类来实现类似效果。
先看第一种实现。
private void animStart2() {
MyView2 myView2 = findViewById(R.id.myview2);
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(myView2, "angle", 0, 360);
objectAnimator.setDuration(5000);
objectAnimator.start();
}
public class MyView2 extends View {
private Paint mCirclePaint;
private Paint mArcPaint;
private float angle;
private RectF mRectF;
public MyView2(Context context) {
this(context, null);
}
public MyView2(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, -1);
}
public MyView2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePaint.setStyle(Paint.Style.FILL_AND_STROKE);
mCirclePaint.setStrokeWidth(2.0f);
mCirclePaint.setColor(Color.GRAY);
mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mArcPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mArcPaint.setStrokeWidth(2.0f);
mArcPaint.setColor(Color.RED);
mRectF = new RectF(0, 0, 200, 200);
}
public float getAngle() {
return angle;
}
public void setAngle(float angle) {
this.angle = angle;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(mRectF.centerX(), mRectF.centerY(), mRectF.height() / 2, mCirclePaint);
canvas.drawArc(mRectF, 180, angle, true, mArcPaint);
}
}
还有一种通过包装类来实现,比如我想设置Button宽度的动画。代码如下
public class ViewWrapper {
private View mTarget;
private int width;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
public ViewWrapper(View mTarget) {
this.mTarget = mTarget;
}
}
private void animStart3() {
ViewWrapper viewWrapper = new ViewWrapper(btn);
ObjectAnimator objectAnimator = ObjectAnimator.ofInt(viewWrapper, "width", btn.getWidth(), 800);
objectAnimator.setDuration(5000);
objectAnimator.start();
}
ObjectAnimator 和 ValueAnimator 区别
通过上面的例子可以知道,ObjectAnimator的实现底层就是ValueAnimator,ObjectAnimator的使用更加简单,可以直接设置属性开启动画并不需要其他额外操作。但是ValueAnimator还需要监听更新进度添加监听addUpdateListener(),根据值的不同,来实现动画。
插值器
作用:设置属性值从初始值过渡到结束值的变化规律,速度等,实现非线性的动画效果。
可以实现Interpolator 接口,实现getInterpolation的方法,返回fraction,估值器用于计算的百分比。
估值器
通过插值器的fraction,开始值和结束值,来计算出当前具体属性值。