深圳幻海软件技术有限公司 欢迎您!

动画 ViewPropertyAnimator 使用详解和原理分析

2023-02-28

本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。前言平常所做的动画大部分是针对View的,而View经常会需要集中动画混合在一起做,因此提供了一个ViewPropertyAnimator类来快速的实现多个动画的混合;ViewPr

本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。

前言

平常所做的动画大部分是针对View的,而View经常会需要集中动画混合在一起做,因此提供了一个ViewPropertyAnimator类来快速的实现多个动画的混合;

ViewPropertyAnimator从名字就可以看出是专用于View的属性动画,在API12被提供;

ViewPropertyAnimator专用于操作View动画,语法更加简洁,使用更加方便;

今天就来看看怎么用;

一、ViewPropertyAnimator使用详解

1、获取对象

ViewPropertyAnimator 没有构造函数,通过View.animate()方法可以方便的获取;ViewPropertyAnimator 对象,此时获取的动画对象就专用于操作当前view;

public ViewPropertyAnimator animate() { 
    if (mAnimator == null) { 
        mAnimator = new ViewPropertyAnimator(this); 
    } 
    return mAnimator; 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

2、基本函数属性介绍

  • alpha(float value) 设置View的透明度,value最终值;
  • alphaBy(float value) 设置View的透明度,value是在view当前值的基础上的偏移量;rotation(float value):旋转View,正值顺时针,负值逆时针,value最终值;
  • rotationBy(float value):旋转,在当前值得基础上偏移量;
  • rotationX(float value):绕x轴旋转;
  • rotationXBy(float value):当View旋转的基础上以value为偏移量绕X轴旋转;
  • rotationY(float value):绕Y轴旋转;
  • rotationYBy(float value):在当前旋转的基础上绕Y轴旋转;
  • scaleX(float value):缩放view的X轴方向上的大小;
  • scaleXBy(float value):当前View缩放的基础上,在X轴方向上对view进行缩放;
  • scaleY(float value):缩放view的Y轴方向上的大小;
  • scaleYBy(float value):当前View缩放的基础上,对view的Y轴方向进行缩放;
  • translationX(float value):沿X轴方向平移,value大于0,X轴正方向;
  • translationXBy(float value):带有偏移量的平移;
  • translationY(float value):沿Y轴方向平移,value大于0,沿Y轴正方向平移;
  • translationYBy(float value) :在当前值的基础上,在Y轴方向上平移;
  • x(float value):在当前值的基础上,修改view 的X坐标;
  • xBy(float value):在当前值的基础上,修改view 的X坐标;
  • y(float value):在当前值的基础上,修改View的Y的坐标;
  • yBy(float value):在当前值的基础上,修改View的Y的坐标;
  • z(float value):在当前值的基础上,修改View的Z的坐标;
  • zBy(float value):在当前值的基础上,修改View的Z的坐标;

3、基本使用

常用方法

btnShow.animate() 
                        .setDuration(5000) 
                        //透明度 
                        .alpha(0) 
                        .alphaBy(0) 
                        //旋转 
                        .rotation(360) 
                        .rotationBy(360) 
                        .rotationX(360) 
                        .rotationXBy(360) 
                        .rotationY(360) 
                        .rotationYBy(360) 
                        //缩放 
                        .scaleX(1) 
                        .scaleXBy(1) 
                        .scaleY(1) 
                        .scaleYBy(1) 
                        //平移 
                        .translationX(100) 
                        .translationXBy(100) 
                        .translationY(100) 
                        .translationYBy(100) 
                        .translationZ(100) 
                        .translationZBy(100) 
                        //更改在屏幕上的坐标 
                        .x(10) 
                        .xBy(10) 
                        .y(10) 
                        .yBy(10) 
                        .z(10) 
                        .zBy(10) 
                        //监听及其他设置 
                        .setInterpolator(new BounceInterpolator()) 
                        .setStartDelay(1000) 
                        .setListener(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) { 
                            } 
                        }) 
                        .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 
                            @Override 
                            public void onAnimationUpdate(ValueAnimator animation) { 
                            } 
                        }) 
                        .withEndAction(new Runnable() { 
                            @Override 
                            public void run() { 
                                Log.i(TAG, "run: end"); 
                            } 
                        }) 
                        .withStartAction(new Runnable() { 
                            @Override 
                            public void run() { 
                                Log.i(TAG, "run: start"); 
                            } 
                        }) 
                        .start(); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.

4、添加监听

  • setUpdateListener:添加动画属性变化监听
  • setListener:添加动画状态监听
ViewPropertyAnimator viewPropertyAnimator = gongxiang.animate().setDuration(3000).x(700).y(700).rotation(270).alpha(0.5f).setUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 
    @Override 
    public void onAnimationUpdate(ValueAnimator animation) { 
        ); 
    } 
}).setListener(new AnimatorListenerAdapter() { 
    @Override 
    public void onAnimationCancel(Animator animation) { 
        super.onAnimationCancel(animation); 
    } 
    @Override 
    public void onAnimationEnd(Animator animation) { 
        super.onAnimationEnd(animation); 
        System.out.println("=========onAnimationEnd======="); 
    } 
    @Override 
    public void onAnimationRepeat(Animator animation) { 
        super.onAnimationRepeat(animation); 
    } 
    @Override 
    public void onAnimationStart(Animator animation) { 
        super.onAnimationStart(animation); 
        System.out.println("=========onAnimationStart======="); 
    } 
    @Override 
    public void onAnimationPause(Animator animation) { 
        super.onAnimationPause(animation); 
    } 
    @Override 
    public void onAnimationResume(Animator animation) { 
        super.onAnimationResume(animation); 
    } 
}); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

二、基本原理

1、执行动画基本步骤如下

  • 通过test.animate()获取ViewPropertyAnimator对象;
  • 调用alpha、translationX等方法,返回当前ViewPropertyAnimator对象,可以继续链式调用;
  • alpha、translationX等方法内部最终调用animatePropertyBy(int constantName, float startValue, float byValue)方法;
  • 在animatePropertyBy方法中则会将alpha、translationX等方法的操作封装成NameVauleHolder,并将每个NameValueHolder对象添加到准备列表mPendingAnimations中;
  • animatePropertyBy方法启动mAnimationStarter,调用startAnimation,开始动画;
  • startAnimation方法中会创建一个ValueAnimator对象设置内部监听器;AnimatorEventListener,并将mPendingAnimations和要进行动画的属性名称封装成一个PropertyBundle对象,最后mAnimatorMap保存当前Animator和对应的PropertyBundle对象,该Map将会在animatePropertyBy方法和Animator监听器mAnimatorEventListener中使用,启动动画;
  • 在动画的监听器的onAnimationUpdate方法中设置所有属性的变化值,并通过RenderNode类优化绘制性能,最后刷新界面;

2、startAnimation()的源码

/** 
 * Starts the underlying Animator for a set of properties. We use a single animator that 
 * simply runs from 0 to 1, and then use that fractional value to set each property 
 * value accordingly. 
 */ 
private void startAnimation() { 
    if (mRTBackend != null && mRTBackend.startAnimation(this)) { 
        return
    } 
    mView.setHasTransientState(true); 
    //创建ValueAnimator 
    ValueAnimator animator = ValueAnimator.ofFloat(1.0f); 
    //clone一份mPendingAnimations赋值给nameValueList 
    ArrayList<NameValuesHolder> nameValueList = 
            (ArrayList<NameValuesHolder>) mPendingAnimations.clone(); 
     //赋值完后清空 
    mPendingAnimations.clear(); 
    //用于标识要执行动画的属性 
    int propertyMask = 0; 
    int propertyCount = nameValueList.size(); 
    //遍历所有nameValuesHolder,取出其属性名称mNameConstant, 
    //执行"|"操作并最终赋值propertyMask 
    for (int i = 0; i < propertyCount; ++i) { 
        NameValuesHolder nameValuesHolder = nameValueList.get(i); 
        propertyMask |= nameValuesHolder.mNameConstant; 
    } 
    //创建PropertyBundle,并添加到mAnimatorMap中 
    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(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 创建Animator,变化值从0到1,设置内部监听器mAnimatorEventListener;
  • clone一份mPendingAnimations列表,并计算属性值标记propertyMask,封装成PropertyBundle对象;
  • 使用mAnimatorMap保存当前Animator和对应的PropertyBundle对象;
  • 该Map将会在animatePropertyBy方法和Animator监听器mAnimatorEventListener中使用;
  • 启动animator动画;

2、PropertyBundle

private static class PropertyBundle { 
        int mPropertyMask; 
        ArrayList<NameValuesHolder> mNameValuesHolder; 
        PropertyBundle(int propertyMask, ArrayList<NameValuesHolder> nameValuesHolder) { 
            mPropertyMask = propertyMask; 
            mNameValuesHolder = nameValuesHolder; 
        } 
        boolean cancel(int propertyConstant) { 
            if ((mPropertyMask & propertyConstant) != 0 && mNameValuesHolder != null) { 
                int count = mNameValuesHolder.size(); 
                for (int i = 0; i < count; ++i) { 
                    NameValuesHolder nameValuesHolder = mNameValuesHolder.get(i); 
                    if (nameValuesHolder.mNameConstant == propertyConstant) { 
                        mNameValuesHolder.remove(i); 
                        mPropertyMask &= ~propertyConstant; 
                        return true
                    } 
                } 
            } 
            return false
        } 
    } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

PropertyBundle:内部类,存放着将要执行的动画的属性集合信息,每次调用animator.start();

都会将存放在mPendingAnimations的clone一份存入PropertyBundle的内部变量mNameValuesHolder中,然后再将遍历mPendingAnimations中的NameValueHolder类,取出要执行的属性进行”|”操作;

最后记录成一个mPropertyMask的变量,存放在PropertyBundle中,PropertyBundle就是最终要执行动画的全部属性的封装类;

3、AnimatorEventListener

private class AnimatorEventListener 
            implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener { 
... 
... 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

ViewPropertyAnimator内部的监听器:这个类实现了Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener接口;

这个类还有一个onAnimationUpdate()的监听方法,它是动画执行的关键所在;

4、mAnimatorMap

private HashMap<Animator, PropertyBundle> mAnimatorMap = 
        new HashMap<Animator, PropertyBundle>(); 
  • 1.
  • 2.
  • mAnimatorMap:存放PropertyBundle类的Map,这个Map中存放的是正在执行的动画的PropertyBundle,这个PropertyBundle包含这本次动画的所有属性的信息;
  • 最终在AnimatorEventListener的onAnimationUpdate()方法中会通过这个map获取相应的属性,然后不断更新每帧的属性值以达到动画效果;
  • 通过前面对animatePropertyBy方法的分析,我们可以知道该Map会保证当前只有一个Animator对象对该View的属性进行操作,不会存在两个Animator在操作同一个属性;

5、onAnimationUpdate

@Override 
        public void onAnimationUpdate(ValueAnimator animation) { 
        //取出当前Animator对应用propertyBundle对象 
            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 setand how alpha is handled when it is 
            // setand 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); 
            } 
            //取出所有要执行的属性动画的封装对象NameValuesHolder 
            ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder; 
            if (valueList != null) { 
                int count = valueList.size(); 
                //遍历所有NameValuesHolder,计算变化值,并设置给对应的属性 
                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(falsein all cases except if alphaHandled gets set to true 
            // via the call to setAlphaNoInvalidation(), above 
            if (alphaHandled) { 
                mView.invalidate(true); 
            } else { 
                mView.invalidateViewProperty(falsefalse); 
            } 
            if (mUpdateListener != null) { 
                mUpdateListener.onAnimationUpdate(animation); 
            } 
        } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.

取出当前Animator对应用propertyBundle对象并获取当前的估算值(插值器计算值),用于后续动画属性值的计算;

从propertyBundle取出要进行动画的属性列表 ArrayList valueList;

遍历所有NameValuesHolder,计算变化值,并通过setValue设置给对应的属性,如果是ALPHA,则会特殊处理一下,最终形成动画效果;

总结

 

年底了,有疫情有裁员,大家要努力,一起加油