ViewPropertyAnimator源码解析

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()等,这些方法比较简单,就不一一介绍了。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值