属性动画随笔

属性动画的实现机制:通过对目标对象进行赋值并修改其属性来实现的,其中可以是任意对象的任意属性;运行机制:通过不断地对值进行操作来实现的。工作原理:要求目标对象提供该属性的set()方法,属性动画会根据你传递的该属性的初始值和结束值,系统内部会调用相应的TypeEvaluator实现类进行值估算,以动画的效果多次去调用set()方法去对该属性不断赋值进行修改以达到实际的效果。ps:如果没有给定初始值,则同样需要提供该属性的get()方法。
一、属性动画相关的类:
Animator:顶层父类,是一个抽象类
ValueAnimator(数值动画):Animator的实现类(继承自Animator),是属性动画机制中最核心的一个类,初始值和结束值之间的动画过度就是由ValueAnimator这个类负责计算的,其内部使用一种时间循环的机制来计算值与值之间的动画过度,使用时,我们只需要提供初始值和结束值给ValueAnimator,并且告诉它动画运行的时长,那么ValueAnimator就会自动完成从初始值平滑地过渡到结束值这样的效果;
1、例如从0f到1f之间的动画过渡:

ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
        animator.setDuration(5000);
        animator.start();

首先,ValueAnimator的ofFloat()方法会构建出一个ValueAnimator实例,该方法允许传入多个 float类型参数;这里给定初始值 0f 和结束值 1f ,表示动画从0平滑过渡到1;但是我们并不会看到任何效果,因为我们没有指定对象,单纯地对值进行计算。
2、监听动画值大小的改变:在该监听器中,可以进行一些伴随操作,如

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Log.e("ValueAnimator", (float)animation.getAnimatedValue() + "");
            }
        });

ObjectAnimator(对象动画):继承自ValueAnimator,我们一般都是用该类进行动画的操作,因为它可以直接地对任意对象的任意属性进行动画操作。提供ofFloat()、ofInt()、ofObject()方法。使用时,必须满足以下三个条件:
1):对象的属性应该提供set方法。
2):需要指定目标对象,属性及属性值(最少一个),比如说View的alpha属性。
3):如果属性有get方法,则返回值必须和set方法的参数类型一致。

使用方法:

1.直接使用:如
1):将textView在5s中从常规变成透明,再从透明变成常规:

ObjectAnimator animator = ObjectAnimator.ofFloat(textView, "alpha", 1f, 0f, 1f);
        animator.setDuration(5000);
        animator.start();

这里第一个参数要求传入一个Object对象,我们想对哪个对象进行动画操作就传入哪个;第二个参数要求传入对象的任意属性,如:透明度 alpha,旋转rotation,平移translation,缩放scale,颜色color,背景颜色backgroundColor等;在这两个参数后面还可以传入多个float型数据(最少一个),1个表示结束值,开始值系统会自动通过get()方法获取;
2):将textView在5s中旋转360度:

ObjectAnimator rotation = ObjectAnimator.ofFloat(textView, "rotation", 0f, 360f);
 rotation.setDuration(5000);
 rotation.start();

3):将textView在5s中从常规收缩成没有,再从没有放大成常规:

ObjectAnimator scaleX = ObjectAnimator.ofFloat(textView, "scaleX", 1f, 0f, 1f);
scaleX.setDuration(5000);
scaleX.start();

4):将textView在5s中从原始位置平移800px,再平移到原始位置:

float translationX = textView.getTranslationX();
        ObjectAnimator translationY1 = ObjectAnimator.ofFloat(textView, "translationX",
                translationX, 800f, translationX);
translationY1.setDuration(4500);
translationY1.start();

5):将textView在5s中,进行一组动画操作:

ObjectAnimator animator = ObjectAnimator.ofFloat(textView, "alpha", 0.5f, 1f);
ObjectAnimator rotation = ObjectAnimator.ofFloat(textView, "rotation", 0, 360);
ObjectAnimator scaleX = ObjectAnimator.ofFloat(textView, "scaleX", 1f, 3f, 1.5f, 1f);

AnimatorSet set = new AnimatorSet();   set.play(translationY1).with(animator).with(rotation).before(scaleX);
set.setDuration(800);
set.start();

组合动画是通过AnimatorSet类进行控制的,它提供了如下方法:提供play()、with()、after()、before()方法,play():播放动画,with:同时,after:在某Animator之后执行,before:在某Animator之前执行

2.使用xml文件来创建属性动画
在res文件夹下创建一个子文件夹animator,然后在该文件夹里面创建动画xml文件

<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:ordering="sequentially">

    <set
        android:ordering="together">
        <objectAnimator
            android:duration="2000"
            android:propertyName="translationX"
            android:valueFrom="-300"
            android:valueTo="30"
            android:valueType="floatType"
            />
        <objectAnimator
            android:duration="2000"
            android:propertyName="alpha"
            android:valueFrom="0"
            android:valueTo="1"
            android:valueType="floatType"
            />
    </set>

    <set
        android:ordering="together">
        <objectAnimator
            android:duration="3000"
            android:propertyName="rotation"
            android:valueFrom="0"
            android:valueTo="360"
            android:valueType="floatType"
            />
        <objectAnimator
            android:duration="2000"
            android:propertyName="scaleX"
            android:valueFrom="1"
            android:valueTo="2"
            android:valueType="floatType"
            />
    </set>
</set>

在这里set表示:AnimatorSet,有两个属性sequentially:按先后顺序执行、together:同步执行。 animator表示:ValueAnimator、
ObjectAnimator表示:ObjectAnimator
5):监听动画状态改变事件:

set.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) {

            }
        });

状态主要有:开始、结束、取消、重复;但是,有时候我们只需要监听动画结束状态,则其余的状态就显得有点多余了。所以我们可以使用AnimatorListenerAdapter类,该类实现了AnimatorListener接口,但是并没有对接口的方法进行操作,只是一个空方法,所以我们创建AnimatorListenerAdapter实例时,可以选择性地重写自己需要的状态监听方法(我们写代码时,可以参考这种模式:当一个接口有多个状态时,创建一个抽象类实现该接口,重写该接口所有方法,但是不对该方法进行操作(空实体),当我们需要该接口监听时,只需要创建抽象类实例,选择性重写自己需要的监听方法)。

 set.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                Toast.makeText(MainActivity.this, "动画结束", Toast.LENGTH_SHORT).show();
            }
        });

AnimatorSet:用于控制一组动画的执行:先后顺序,同步顺序等
AnimatorInflater:加载属性动画的xml文件

TypeEvaluator:估值器,计算初始值到结束值的平滑过渡(不断改变对象的属性值,通过set() 方法)。一般情况下,我们是不需要自定义TypeEvaluator,官方默认提供的也够用了。但是我们自定义时,操作对象必须提供属性的set()方法,为了方便,一般都会提供get()方法
TypeEvaluator源码:

public interface TypeEvaluator<T> {
    public T evaluate(float fraction, T startValue, T endValue);
}

只提供了一个evaluate()方法,系统提供三个参数:fraction:估值小数(范围为0~1)、startValue:开始值,endValue:结束值;算法如下所示:
startValue+fraction*(endValue - startValue);如果修改对象有多个属性,则分别对其属性进行赋值,如下所示:

  @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        Point startPoint = (Point) startValue;
        Point endPoint = (Point) endValue;
        float x = startPoint.getX() + fraction *(endPoint.getX() - startPoint.getX());
        float y = startPoint.getY() + fraction *(endPoint.getY() - startPoint.getY());
        Point point = new Point(x, y);
        return point;
    }

TimeInterpolator:插值器,定义动画的变化率;源码如下所示:

public interface TimeInterpolator {
    float getInterpolation(float input);
}

对象属性的get和set方法的调用:系统内部通过反射机制(类Property)获取到该对象属性的set/get方法,

源码分析,如:.ofInt(view, “scaleX”, 0f):

1.ofInt

 public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setIntValues(values);
        return anim;
    }

这里首先调用构造方法,传入target,propertyName参数,创建出ObjectAnimator对象,同时记录下target动画执行对象、propertyName对象的属性,如下所示:

private ObjectAnimator(Object target, String propertyName) {
        setTarget(target);
        setPropertyName(propertyName);
    }

@Override
public void setTarget(@Nullable Object target) {
     final Object oldTarget = getTarget();
     if (oldTarget != target) {
         if (isStarted()) {
             cancel();
            }
       mTarget = target == null ? null : new WeakReference<Object>(target);
            // New target should cause re-initialization prior to starting
            mInitialized = false;
        }
    }

public void setPropertyName(@NonNull String propertyName) {
        // mValues could be null if this is being constructed piecemeal. Just record the
        // propertyName to be used later when setValues() is called if so.
        if (mValues != null) {
            PropertyValuesHolder valuesHolder = mValues[0];
            String oldName = valuesHolder.getPropertyName();
            valuesHolder.setPropertyName(propertyName);
            mValuesMap.remove(oldName);
            mValuesMap.put(propertyName, valuesHolder);
        }
        mPropertyName = propertyName;
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

1):记录target、propertyName完成后,调用setIntValues方法:

@Override
    public void setIntValues(int... values) {
        if (mValues == null || mValues.length == 0) {
            // No values yet - this animator is being constructed piecemeal. Init the values with
            // whatever the current propertyName is
            if (mProperty != null) {
                setValues(PropertyValuesHolder.ofInt(mProperty, values));
            } else {
                setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
            }
        } else {
            super.setIntValues(values);
        }
    }

这里可以看出,该方法将属性名mPropertyName和值values传入到PropertyValuesHolder的ofInt方法中,去构造一个PropertyValuesHolder实例:

public static PropertyValuesHolder ofInt(String propertyName, int... values) {
        return new IntPropertyValuesHolder(propertyName, values);
    }
public IntPropertyValuesHolder(String propertyName, int... values) {
            super(propertyName);
            setIntValues(values);
        }
private PropertyValuesHolder(String propertyName) {
        mPropertyName = propertyName;
    }
public void setIntValues(int... values) {
        mValueType = int.class;
        mKeyframes = KeyframeSet.ofInt(values);
    }

这里可以看出,PropertyValuesHolder内部存储了我们的属性名propertyName,然后又调用setIntValues方法存储了属性类型mValueType,同时将values值传给了KeyframeSet.ofInt(values)方法,构建出一个KeyframeSet实例:

 public static KeyframeSet ofInt(int... values) {
        int numKeyframes = values.length;
        IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
        if (numKeyframes == 1) {
        //调用Keyframes.ofInt方法构建Keyframes实例
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
            keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
        } else {
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
                keyframes[i] =
                        (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
            }
        }
        return new IntKeyframeSet(keyframes);
    }


public KeyframeSet(Keyframe... keyframes) {
        //关键帧个数
        mNumKeyframes = keyframes.length;
        // immutable list
        mKeyframes = Arrays.asList(keyframes);
        //开始帧
        mFirstKeyframe = keyframes[0];
        //结束帧
        mLastKeyframe = keyframes[mNumKeyframes - 1];
        //插值器
        mInterpolator = mLastKeyframe.getInterpolator();
    }


public static Keyframe ofInt(float fraction) {
        return new IntKeyframe(fraction);
    }

IntKeyframe(float fraction, int value) {
            mFraction = fraction;
            mValue = value;
            mValueType = int.class;
            mHasValue = true;
        }

这里可以看出:该方法先调用Keyframe的ofInt方法创建出Keyframe实例,同时存储了fraction、value;然后将初始化完成的Keyframes数组传传入KeyframeSet的构造方法中,初始化了其内部的参数,并进行存储了mNumKeyframes 关键帧个数,mFirstKeyframe 开始帧,mLastKeyframe 结束帧,mInterpolator 插值器

至此,PropertyValuesHolder的ofInt方法执行完毕,然后将创建出的PropertyValuesHolder对象传入到setValues方法中:

public void setValues(PropertyValuesHolder... values) {
        int numValues = values.length;
        mValues = values;
        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
        for (int i = 0; i < numValues; ++i) {
            PropertyValuesHolder valuesHolder = values[i];
            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
        }
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

在该方法中,首先记录了mValues,且该mValues是PropertyValuesHolder类型,然后通过mValuesMap存储:以属性名propertyName为key,值为PropertyValuesHolder;
———————————————分割线—————————————————————
至此,ofInt方法执行完毕,主要作用是:存储了 动画执行对象 target、对象属性名 mPropertyName、 mValues(PropertyValuesHolder类型)及mValuesMap,这个map的key为属性名mPropertyName,值为mValues。

而PropertyValuesHolder中存储了KeyframeSet(Keyframe的集合)等。

2.setInterpolator

 @Override
    public void setInterpolator(TimeInterpolator value) {
        if (value != null) {
            mInterpolator = value;
        } else {
            mInterpolator = new LinearInterpolator();
        }
    }

默认是线性插值器

3.setEvaluato

public void setEvaluator(TypeEvaluator value) {
        if (value != null && mValues != null && mValues.length > 0) {
            mValues[0].setEvaluator(value);
        }
    }

该方法调用了PropertyValuesHolder的setEvaluator方法

 public void setEvaluator(TypeEvaluator evaluator) {
        mEvaluator = evaluator;
        mKeyframes.setEvaluator(evaluator);
    }

先记录了估值算法,再传递给KeyframeSet

public void setEvaluator(TypeEvaluator evaluator) {
        mEvaluator = evaluator;
    }

层层传递,将估值算法传给了PropertyValuesHolder和KeyframeSet

4.setDuration

 @Override
    public ValueAnimator setDuration(long duration) {
        if (duration < 0) {
            throw new IllegalArgumentException("Animators cannot have negative duration: " +
                    duration);
        }
        mUnscaledDuration = duration;
        updateScaledDuration();
        return this;
    }

private void updateScaledDuration() {
        mDuration = (long)(mUnscaledDuration * sDurationScale);
    }

该方法就是简单记录了动画持续的时间

5.start

 private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        mReversing = playBackwards;
        mPlayingBackwards = playBackwards;
        if (playBackwards && mSeekFraction != -1) {
            if (mSeekFraction == 0 && mCurrentIteration == 0) {
                // special case: reversing from seek-to-0 should act as if not seeked at all
                mSeekFraction = 0;
            } else if (mRepeatCount == INFINITE) {
                mSeekFraction = 1 - (mSeekFraction % 1);
            } else {
                mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction);
            }
            mCurrentIteration = (int) mSeekFraction;
            mSeekFraction = mSeekFraction % 1;
        }
        if (mCurrentIteration > 0 && mRepeatMode == REVERSE &&
                (mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) {
            // if we were seeked to some other iteration in a reversing animator,
            // figure out the correct direction to start playing based on the iteration
            if (playBackwards) {
                mPlayingBackwards = (mCurrentIteration % 2) == 0;
            } else {
                mPlayingBackwards = (mCurrentIteration % 2) != 0;
            }
        }
        int prevPlayingState = mPlayingState;
        mPlayingState = STOPPED;
        mStarted = true;
        mStartedDelay = false;
        mPaused = false;
        updateScaledDuration(); // in case the scale factor has changed since creation time
        AnimationHandler animationHandler = getOrCreateAnimationHandler();
        animationHandler.mPendingAnimations.add(this);
        if (mStartDelay == 0) {
            // This sets the initial value of the animation, prior to actually starting it running
            if (prevPlayingState != SEEKED) {
                setCurrentPlayTime(0);
            }
            mPlayingState = STOPPED;
            mRunning = true;
            notifyStartListeners();
        }
        animationHandler.start();
    }

该方法对上述属性进行合理地调用

前面提到,属性动画的运行机制是不断地对任意对象的任意属性进行赋值并修改而实现的,而属性的赋值是通过对象属性的set方法调用实现,下面看一下set方法的调用:

void setAnimatedValue(Object target) {
        if (mProperty != null) {
            mProperty.set(target, getAnimatedValue());
        }
        if (mSetter != null) {
            try {
                mTmpValueArray[0] = getAnimatedValue();
                mSetter.invoke(target, mTmpValueArray);
            } catch (InvocationTargetException e) {
                Log.e("PropertyValuesHolder", e.toString());
            } catch (IllegalAccessException e) {
                Log.e("PropertyValuesHolder", e.toString());
            }
        }
    }

从该方法可以看出:对象属性的set方法是通过反射机制来获取到的,当动画的下一帧到来的时候,PropertyValuesHolder中的setAnimatedValue方法将会新的属性值设置给对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值