Android 动画之属性动画ValueAnimator和ObjectAnimator

来源:互联网 发布:mybatis 源码 编辑:程序博客网 时间:2024/06/05 03:34

简介

属性动画是API 11新加入的特性,和View动画不同,它对作用对象进行了扩展,属性动画可以对任意对象做动画,也不像View动画只支持四种简单的变化。

属性动画的默认时间是300ms,默认频率是10ms/帧

ObjectAniamtor

先看看一个简单例子的实现效果,使用ObjectAnimator,它使用起来比较简单


布局就是一个ImageView,看看实现代码

 //透明度变化动画    public void alpha(View view) {//        1、通过调用ofFloat()方法创建ObjectAnimator对象,并设置目标对象、需要改变的目标属性名、初始值和结束值;        ObjectAnimator mAnimatorAlpha = ObjectAnimator.ofFloat(mImageView, "alpha", 1.0f, 0.0f);        //2、设置动画的持续时间、是否重复及重复次数属性;        mAnimatorAlpha.setRepeatMode(Animation.REVERSE);        mAnimatorAlpha.setRepeatCount(1);        mAnimatorAlpha.setDuration(1000);        //3、启动动画        mAnimatorAlpha.start();    }        //翻转动画,翻转360度    public void flip(View view) {        ObjectAnimator visibleToInVisable = ObjectAnimator.ofFloat(mImageView, "rotationX", 0.0f, 360.0f);        //设置插值器        visibleToInVisable.setInterpolator(new LinearInterpolator());        visibleToInVisable.setDuration(1000);        visibleToInVisable.start();    }    //缩放动画    public void scale(View view) {        Animator animator = AnimatorInflater.loadAnimator(this, R.animator.scale_anim);        animator.setTarget(mImageView);        animator.start();    }        //平移动画    public void translate(View view) {        ObjectAnimator mAnimatorTranslateX = ObjectAnimator.ofFloat(mImageView, "translationX", 0.0f, screenWidth / 2);        mAnimatorTranslateX.setRepeatMode(Animation.REVERSE);        mAnimatorTranslateX.setRepeatCount(1);        mAnimatorTranslateX.setDuration(1000);        ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(mImageView, "translationY", 0.0f, screenHeight / 2);        mAnimatorTranslateY.setRepeatMode(Animation.REVERSE);        mAnimatorTranslateY.setRepeatCount(1);        mAnimatorTranslateY.setDuration(1000);        mAnimatorTranslateX.start();        mAnimatorTranslateY.start();    }    //旋转动画    public void rotate(View view) {        ObjectAnimator mAnimatorRotate = ObjectAnimator.ofFloat(mImageView, "rotation", 0.0f, 360.0f);        mAnimatorRotate.setRepeatMode(Animation.REVERSE);        mAnimatorRotate.setRepeatCount(1);        mAnimatorRotate.setDuration(2000);        mAnimatorRotate.start();    }


就是根据ObjectAnimator的ofFloat(Object target, String propertyName, float... values)构造得到一个ObjectAnimator,可以看到第一个参数是Object类型的,意味着我们可以传入自定义的类型,而不局限于补间动画只能用view来做,扩展性也就更好。第二个参数是动画名称,可以传递系统定义好的,比如上面的几个,也可以自己随便写,然后在属性变化的监听里改变。第三个参数就是属性值,如果只有一个的话会默认为结束值。

上面缩放动画是用xml实现的,需要在res下面建一个animator文件夹,然后创建相应的xml动画,如下

<?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:repeatCount="0"        android:repeatMode="reverse"        android:startOffset="500"        android:valueFrom="0.1f"        android:valueTo="1.0f"        android:valueType="floatType" /></set>


组合动画

现在要多个动画效果一起实现怎么办,有下面几种方式

1.PropertyValuesHolder

 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0.0f, 1.0f);        PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.0f);        ObjectAnimator animator1 = ObjectAnimator.ofPropertyValuesHolder(mImageView, alpha, scaleX);        animator1.setDuration(1000);        animator1.start();
2.监听属性变化

 ObjectAnimator animator = ObjectAnimator.ofFloat(mImageView, "lzy", 0.0f, 1.0f);        animator.setDuration(1000);        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                float value = (float) animation.getAnimatedValue();                mImageView.setAlpha(value);                mImageView.setScaleX(1 - value * 0.5f);            }        });        animator.start();
设置属性变化值在0-1之间,然后在onAnimationUpdate中监听得到,通过这个值调用view自身的方法改变。

3.AnimatorSet

AnimatorSet mAnimatorSet = new AnimatorSet();        ObjectAnimator mAnimatorSetRotateX = ObjectAnimator.ofFloat(mImageView, "rotationX", 0.0f, 360.0f);        mAnimatorSetRotateX.setDuration(3000);        ObjectAnimator mAnimatorSetRotateY = ObjectAnimator.ofFloat(mImageView, "rotationY", 0.0f, 360.0f);        mAnimatorSetRotateY.setDuration(3000);        ObjectAnimator mAnimatorScaleX = ObjectAnimator.ofFloat(mImageView, "scaleX", 1.0f, 0.2f);        mAnimatorScaleX.setRepeatCount(1);        mAnimatorScaleX.setRepeatMode(Animation.REVERSE);        mAnimatorScaleX.setDuration(1500);        ObjectAnimator mAnimatorScaleY = ObjectAnimator.ofFloat(mImageView, "scaleY", 1.0f, 0.2f);        mAnimatorScaleY.setRepeatCount(1);        mAnimatorScaleY.setRepeatMode(Animation.REVERSE);        mAnimatorScaleY.setDuration(1500);        ObjectAnimator mAnimatorScaleY2 = ObjectAnimator.ofFloat(mImageView, "scaleY", 1.0f, 0.2f);        mAnimatorScaleY2.setRepeatCount(1);        mAnimatorScaleY2.setRepeatMode(Animation.REVERSE);        mAnimatorScaleY2.setDuration(1500);        mAnimatorSet.play(mAnimatorSetRotateY)                .with(mAnimatorScaleX)                .with(mAnimatorScaleY)                .before(mAnimatorSetRotateX).before(mAnimatorScaleY2);        mAnimatorSet.start();
通过animationSet来实现,它提供了一个play()方法,传入Animator返回一个Builder,这个Builder中有以下四个方法

    with(Animator anim),表示同时执行

    before(Animator anim),表示将现有动画插入到传入动画之前执行,也就是后执行

    after(Animator anim),与before相反

    after(long delay),表示延迟多久执行

在测试的时候发现不能重复传入一个动画,所以又写了一个mAnimatorScaleY2 。

AnimationSet中还有playTogether(Animator... items)表示同时执行,playSequentially(Animator... items)表示异步执行。


下面做一个让Button增加宽度的动画效果

属性动画的大致原理:属性动画要求动画作用的对象提供该属性的get和set方法,就像上面的"scaleX"属性,它具有getScaleX和setScale方法,属性动画会根据传递的初始值和结束值,去调用set的方法设置当前的值,如果没有传递初始值会调用get方法去获取初始值。

然后Button中并没有我们想要的get和set方法,所以针对这种问题官方文档给出了下面的解决方法

1.如果有权限的话,给对象加上get和set方法

2.用一个类来包装原始对象,间接为其提供get和set方法

3.采用ValueAnimator,监听动画变化过程,自己实现属性的变化

我们现在以第二种方式来实现以上效果,代码如下

 mButton.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                ViewWrapper wrapper = new ViewWrapper(mButton);                ObjectAnimator.ofInt(wrapper, "width", 1000).setDuration(1000).start();            }        });

 //用此类来封装View,间接提供get、set方法    private static class ViewWrapper {        private View mView;        public ViewWrapper(View view) {            mView = view;        }        public void setWidth(int width) {            mView.getLayoutParams().width = width;            mView.requestLayout();        }        public int getWidth() {            return mView.getLayoutParams().width;        }    }
在这个类中提供了width的get和set方法,getWidth()就是获取View当前的宽度,上面说过在没有指定初始值时会调用获得初始值,而setWidth()方法会不断被调用,直至动画结束去改变View的宽度。

ValueAnimator

ValueAnimator是ObjectAnimator的父类,也就可以理解为ObjectAnimator是一个封装好的ValueAnimator,使其使用起来更加的简单。但是作为父类的ValueAnimator也就使用起来更加的灵活多变。ValueAinamtor本身不会作用于任何对象,直接使用不会有动画效果,可以理解为它对一个值做动画,我们通过这个不断变化的值在监听函数中去修改相应的属性。

看看ValueAnimator的几种构造函数:

ofInt(int... values) 

ofArgb(int... values)

ofFloat(float... values)

ofPropertyValuesHolder(PropertyValuesHolder... values)

ofObject(TypeEvaluator evaluator, Object... values)

前三种参数都是初始值到结束值的变化范围,很简单

ofPropertyValuesHolder代表多种Animator的集合,下面会介绍具体的使用

ofObject很明显它的参数类型是Object类型,大多是我们自定义的类型,第一个参数TypeEvaluator 是估值器,因为像上面的int之类的系统都有定义相应的估值器所以不需要我们传入,然后我们自定义的Object并没有,所以需要自己的来写。

来看看系统的IntEvaluator

public class IntEvaluator implements TypeEvaluator<Integer> {       public Integer evaluate(float fraction, Integer startValue, Integer endValue) {        int startInt = startValue;        return (int)(startInt + fraction * (endValue - startInt));    }}
估值器都需要继承自TypeEvaluator,实现里面的evaluate方法,参数分别是完成的百分比,开始值和结束值,文档解释也告诉我们返回的值只需要用结束值减去开始值乘以fraction 然后加上开始值就ok,从而实现值的过渡,所以自定义TypeEvaluator也很简单了。

下面看例子,还是先看效果在撸代码


 private void marginValueAnimator() {        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, width - mImageView.getWidth());        //监听变化过程        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                //获取当前值                int animatedValueMargin = (int) animation.getAnimatedValue();                ViewGroup.MarginLayoutParams                        layoutParams = (ViewGroup.MarginLayoutParams) mImageView.getLayoutParams();                layoutParams.leftMargin = animatedValueMargin;                mImageView.setLayoutParams(layoutParams);            }        });        valueAnimator.setDuration(1000);        valueAnimator.setRepeatCount(3);        valueAnimator.setRepeatMode(ValueAnimator.REVERSE);        valueAnimator.setTarget(mImageView);        valueAnimator.start();    }
这是个简单的设置view距离左边的动画,构造了ValueAnimator后监听它的更新,在onAnimationUpdate中改变属性值,通过animation.getAnimatedValue()获得当前的值,然后用于更新属性状态,实现动画。

public void scaleValueAnimator() {        //1.设置目标属性名及属性变化的初始值和结束值        PropertyValuesHolder mPropertyValuesHolderScaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.0f);        PropertyValuesHolder mPropertyValuesHolderScaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.0f);        ValueAnimator mAnimator = ValueAnimator.ofPropertyValuesHolder(mPropertyValuesHolderScaleX, mPropertyValuesHolderScaleY);        //2.为目标对象的属性变化设置监听器        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                // 3.根据属性名获取属性变化的值分别为ImageView目标对象设置X和Y轴的缩放值                float animatorValueScaleX = (float) animation.getAnimatedValue("scaleX");                float animatorValueScaleY = (float) animation.getAnimatedValue("scaleY");                mImageView.setScaleX(animatorValueScaleX);                mImageView.setScaleY(animatorValueScaleY);            }        });        //4.为ValueAnimator设置自定义的Interpolator        mAnimator.setInterpolator(new CustomInterpolator());        //5.设置动画的持续时间、是否重复及重复次数等属性        mAnimator.setDuration(2000);        mAnimator.setRepeatCount(3);        mAnimator.setRepeatMode(ValueAnimator.REVERSE);        //6.为ValueAnimator设置目标对象并开始执行动画        mAnimator.setTarget(mImageView);        mAnimator.start();    }
这里通过PropertyValuesHolder来控制实现两种属性的变化,其实这里x和y两个方向的缩放值都是1.0f到0.0f,可以用一个ValueAnimator.ofFloat()来完成,这里主要是为了演示
ofPropertyValuesHolder的使用。

首先是要通过PropertyValuesHolder.ofFloat来创建了两个PropertyValuesHolder对象,其中第一个参数是属性名,可以任意取,可以看到在onAnimationUpdate中通过这个属性名来获取不同PropertyValuesHolder的变化值。然后再通过ValueAnimator.ofPropertyValuesHolder来构造ValueAnimator对象就可以,也很容易看懂。

上面那个类似抛物线的圆是采用ValueAnimator.ofObject来实现的。

1.首先要定义一个Point类,里面用于存放x、y坐标

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(int x) {        this.x = x;    }    public float getY() {        return y;    }    public void setY(int y) {        this.y = y;    }}
之前说过要使用ValueAnimator.ofObject需要自定义TypeEvaluator,下面看看定义的PointEvaluator,可以看到和IntEvaluator一样,主要还是计算变化值

public class PointEvaluator implements TypeEvaluator {    //TypeEvaluator简单来说是实现了初始值和结束值的平滑过渡    @Override    public Object evaluate(float fraction, Object startValue, Object endValue) {        Point start = (Point) startValue;        Point end = (Point) endValue;        //开始值减去结束值乘以fraction再加上开始值就是当前的值        float x = start.getX() + fraction * (end.getX() - start.getX());        float y = start.getY() + fraction * (end.getY() - start.getY());        Point point = new Point(x, y);        return point;    }
下面自定义一个CircleView类
public class CircleView extends View {    private static final float Radius = 40.0f;    private Point mPoint;    private Paint mPaint;    public CircleView(Context context) {        super(context);    }    public CircleView(Context context, AttributeSet attrs) {        super(context, attrs);        mPaint = new Paint();        mPaint.setColor(Color.RED);//        mPoint = new Point(50, 50);    }    public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    protected void onDraw(Canvas canvas) {        if (mPoint == null) {            mPoint = new Point(50, 50);            canvas.drawCircle(mPoint.getX(), mPoint.getY(), Radius, mPaint);            startAnimation();        } else {            canvas.drawCircle(mPoint.getX(), mPoint.getY(), Radius, mPaint);        }    }    private void startAnimation() {        //创建开始和结束点坐标        Point start = new Point(50, 50);        Point end = new Point(getWidth(), getHeight());        ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(), start, end);        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                //把每一帧的值传给mPoint,绘制界面                mPoint = (Point) animation.getAnimatedValue();                invalidate();            }        });        animator.setDuration(5000);        animator.start();    }}
这个类也比较简单,主要就是在onDraw里面画一个圆,然后根据Point的变化不断调用onDraw方法来改变圆的位置,实现动画,这也就是ValueAnimator.ofObject的用法。

源码下载地址


0 0
原创粉丝点击