欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ViewPropertyAnimator源码解析

程序员文章站 2022-05-03 20:05:21
...

ViewPropertyAnimator源码解析

Android3.0引入了属性动画。在属性动画中有两个核心类,ValueAnimator和ObjectAnimator,这两个类可以针对任何对象做动画,功能十分强大,在日常开发中多数是对View对象做动画,直接使用ValueAnimator和ObjectAnimator略有些麻烦,Google在Android3.1中有带来了ViewPropertyAnimator,方便开发者做View相关的属性动画。

下面比较一下三个类的使用方式

  • ValueAnimator:

    ValueAnimator valueAnimator = ValueAnimator.ofInt(0,90);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
           int value = (int) animation.getAnimatedValue();
           mIvImage.setRotation(value);
        }
    });
    valueAnimator.setDuration(300);
    valueAnimator.setInterpolator(new LinearInterpolator());
    valueAnimator.start();
    复制代码
  • ObjectAnimator:

    ObjectAnimator animator = ObjectAnimator.ofFloat(mIvImage, "rotationX", 0f, 180f);
    animator.setDuration(300);
    animator.setInterpolator(new AccelerateDecelerateInterpolator());
    animator.start();
    复制代码
  • ViewPropertyAnimator:

    mIvImage.animate().rotationY(180).setDuration(300).setInterpolator(new DecelerateInterpolator()).start();
    复制代码

可以看到封装层级越高代码越少,使用越方便,但适用范围也就越窄。

创建ViewPropertyAnimator

只能通过View的animate类创建,ViewPropertyAnimator的构造方法是默认访问权限,在其他包中无法直接访问。

public ViewPropertyAnimator animate() {
    if (mAnimator == null) {
        mAnimator = new ViewPropertyAnimator(this);
    }
    return mAnimator;
}
复制代码

使用ViewPropertyAnimator

ViewPropertyAnimator提供了一系列的方法来改变View的属性,可以通过链式调用来执行动画。比如:

mIvImage.animate().translationX(100).translationY(100).rotation(360).setDuration(300).start();
复制代码

每一个改变View属性的方法都提供了两个版本,一种是设置属性的最终值,另一种是设置属性的改变值。旋转View可以使用rotation(degree),也可以使用rotationBy(degree),区别就是rotation(degree)会把View旋转到degree设置的角度,而ratationBy(degree)则会使View在当前旋转角度上再旋转degree设置的角度。

源码分析

通过View的animate方法会创建一个ViewPropertyAnimator对象,在这个对象内会持有一个View引用

ViewPropertyAnimator(View view) {
    mView = view;
    view.ensureTransformationInfo();
}
复制代码

调用translation,rotationBy等方法时会调用内部的animateProperty或animatePropertyBy方法

public ViewPropertyAnimator rotation(float value) {
    animateProperty(ROTATION, value);
    return this;
}

public ViewPropertyAnimator rotationBy(float value) {
    animatePropertyBy(ROTATION, value);
    return this;
}
复制代码

animateProperty方法内部实际上也是调用的animatePropertyBy方法,首先获取当前的属性值,根据目标值计算出变化值,再调用animatePropertyBy方法:

private void animateProperty(int constantName, float toValue) {
    float fromValue = getValue(constantName);
    float deltaValue = toValue - fromValue;
    animatePropertyBy(constantName, fromValue, deltaValue);
}
复制代码

改变View属性的方法最终都会执行到animatePropertyBy方法

private void animatePropertyBy(int constantName, float startValue, float byValue) {
    // First, cancel any existing animations on this property
    if (mAnimatorMap.size() > 0) {
        Animator animatorToCancel = null;
        Set<Animator> animatorSet = mAnimatorMap.keySet();
        for (Animator runningAnim : animatorSet) {
            PropertyBundle bundle = mAnimatorMap.get(runningAnim);
            if (bundle.cancel(constantName)) {
                // property was canceled - cancel the animation if it's now empty
                // Note that it's safe to break out here because every new animation
                // on a property will cancel a previous animation on that property, so
                // there can only ever be one such animation running.
                if (bundle.mPropertyMask == NONE) {
                    // the animation is no longer changing anything - cancel it
                    animatorToCancel = runningAnim;
                    break;
                }
            }
        }
        if (animatorToCancel != null) {
            animatorToCancel.cancel();
        }
    }

    NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue);
    mPendingAnimations.add(nameValuePair);
    mView.removeCallbacks(mAnimationStarter);
	mView.postOnAnimation(mAnimationStarter);
}
复制代码

这个方法逻辑也比较清晰,在mAnimatorMap中存储的是正在运行的动画,如果当前有正在运行的动画,会先取消动画的执行。然后会创建一个NameValuesHolder对象,这个对象存储动画的信息,包括动画改编的属性,属性的起始值和变化值,再将NameValuesHolder保存到mPendingAnimations中,等待执行。最后会调用 mView.removeCallbacks(mAnimationStarter)和 mView.postOnAnimation(mAnimationStarter)这两个方法,

mAnimationStarter是一个Runnable对象,run方法中会调用startAnimation()方法开始动画。连续调用这两个方法的效果是,当使用mIvImage.animate().translationX(100).translationY(100).rotation(360)这样的链式调用时,每次调用结束都会发起一个开始动画的请求(postOnAnimation(mAnimationStarter)),在请求之前会取消前一个请求,那么在调用最后一方法时会保留一个开始动画的请求,即使不调用start()方法,动画也会在链式结束后开始执行。

static class NameValuesHolder {
    int mNameConstant;
    float mFromValue;
    float mDeltaValue;
    NameValuesHolder(int nameConstant, float fromValue, float deltaValue) {
        mNameConstant = nameConstant;
        mFromValue = fromValue;
        mDeltaValue = deltaValue;
    }
}

 private Runnable mAnimationStarter = new Runnable() {
     @Override
     public void run() {
         startAnimation();
     }
 };
复制代码

动画真正的开始是在调用startAnimation之后,我们来看下startAnimation的代码

private void startAnimation() {
    if (mRTBackend != null && mRTBackend.startAnimation(this)) {
        return;
    }
    mView.setHasTransientState(true);
    ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
    ArrayList<NameValuesHolder> nameValueList =
        (ArrayList<NameValuesHolder>) mPendingAnimations.clone();
    mPendingAnimations.clear();
    int propertyMask = 0;
    int propertyCount = nameValueList.size();
    for (int i = 0; i < propertyCount; ++i) {
        NameValuesHolder nameValuesHolder = nameValueList.get(i);
        propertyMask |= nameValuesHolder.mNameConstant;
    }
    mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList));
    if (mPendingSetupAction != null) {
        mAnimatorSetupMap.put(animator, mPendingSetupAction);
        mPendingSetupAction = null;
    }
    if (mPendingCleanupAction != null) {
        mAnimatorCleanupMap.put(animator, mPendingCleanupAction);
        mPendingCleanupAction = null;
    }
    if (mPendingOnStartAction != null) {
        mAnimatorOnStartMap.put(animator, mPendingOnStartAction);
        mPendingOnStartAction = null;
    }
    if (mPendingOnEndAction != null) {
        mAnimatorOnEndMap.put(animator, mPendingOnEndAction);
        mPendingOnEndAction = null;
    }
    animator.addUpdateListener(mAnimatorEventListener);
    animator.addListener(mAnimatorEventListener);
    if (mStartDelaySet) {
        animator.setStartDelay(mStartDelay);
    }
    if (mDurationSet) {
        animator.setDuration(mDuration);
    }
    if (mInterpolatorSet) {
        animator.setInterpolator(mInterpolator);
    }
    animator.start();
}
复制代码

在这个方法中首先会创建一个ValueAnimator对象,然后通过mPendingAnimations获取所有的即将改变的属性,通过propertyMask这个int类型保存所有的要改变化的属性,构建一个PropertyBundle对象,保存到mAnimatorMap这个map中。然后会设置一些动画的事件回调,为ValueAnimator设置更新回调,最后开始动画。我们知道ValueAnimator是在UpdateListener根据当前属性值来做动画的。

AnimatorEventlistener是一个内部类,实现了ValueAnimator.AnimatorUpdateListener和Animator.AnimatorListener。我们先看下AnimatorUpdateListener的onAnimationUpdate方法:

public void onAnimationUpdate(ValueAnimator animation) {
    PropertyBundle propertyBundle = mAnimatorMap.get(animation);
    if (propertyBundle == null) {
        // Shouldn't happen, but just to play it safe
        return;
    }

    boolean hardwareAccelerated = mView.isHardwareAccelerated();

    // alpha requires slightly different treatment than the other (transform) properties.
    // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
    // logic is dependent on how the view handles an internal call to onSetAlpha().
    // We track what kinds of properties are set, and how alpha is handled when it is
    // set, and perform the invalidation steps appropriately.
    boolean alphaHandled = false;
    if (!hardwareAccelerated) {
        mView.invalidateParentCaches();
    }
    float fraction = animation.getAnimatedFraction();
    int propertyMask = propertyBundle.mPropertyMask;
    if ((propertyMask & TRANSFORM_MASK) != 0) {
        mView.invalidateViewProperty(hardwareAccelerated, false);
    }
    ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;
    if (valueList != null) {
        int count = valueList.size();
        for (int i = 0; i < count; ++i) {
            NameValuesHolder values = valueList.get(i);
            float value = values.mFromValue + fraction * values.mDeltaValue;
            if (values.mNameConstant == ALPHA) {
                alphaHandled = mView.setAlphaNoInvalidation(value);
            } else {
                setValue(values.mNameConstant, value);
            }
        }
    }
    if ((propertyMask & TRANSFORM_MASK) != 0) {
        if (!hardwareAccelerated) {
            mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation
        }
    }
    // invalidate(false) in all cases except if alphaHandled gets set to true
    // via the call to setAlphaNoInvalidation(), above
    if (alphaHandled) {
        mView.invalidate(true);
    } else {
        mView.invalidateViewProperty(false, false);
    }
    if (mUpdateListener != null) {
        mUpdateListener.onAnimationUpdate(animation);
    }
}
复制代码

略过硬件加速的相关处理,会首先通过mAnimatorMap.get(animator)获取到PropertyBundle对象,再获取到mNameValuesHolder列表,这个列表中保存所有要改变的属性信息。使用一个循环取出每一个属性,通过

float value = values.mFromValue + fraction * values.mDeltaValue 这个公式计算出属性的当前的值,再调用

setValue(values.mNameConstant, value)方法,这个方法会根据不同的属性调用方法设置属性值,完成了属性值的变化。

最后梳理一下ViewPropertyAnimator的执行流程:

1.调用View的animate()方法创建ViewPropertyAnimator对象

2.translation(),alpha(),rotation()等方法内部会调用animatePropertyBy方法,将要变化的属性信息保存到NameValuesHolder中,并将NameValuesHolder保存到mPendingAnimations列表中,等待执行。

3.调用startAnimation()方法,这个方法中会创建一个ValueAnimator对象,并设置动画相关信息。

4.在ValueAnimator的onAnimationUpdate的方法中,获取当前动画的执行进度,计算出各个属性的当前值,并调用View的方法设置到View上,完成属性的改变。

除上面介绍的各个方法外,ViewPropertyAnimator还提供了其他辅助方法,如setStartDelay(),setInterpolator()setListener()等,这些方法比较简单,就不一一介绍了。