属性动画的执行过程

属性动画的执行过程

本文以ObjectAnimator.ofFloat(view, “translationX”, 0,200.0f);为例

使用属性动画

属性动画的使用,最简单的方式如下:

ObjectAnimator animatorBtnY = ObjectAnimator.ofFloat(view, "translationX", 0,200.0f);//平移200
animatorBtnY.setRepeatCount(-1);//重复次数,-1无限重复
animatorBtnY.setRepeatMode(ValueAnimator.REVERSE);//重复模式,反转
animatorBtnY.setDuration(1000);//执行时间
 animatorBtnY.start();//开始
animatorBtnY.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) {
				//重复
                }
}
 animatorBtnY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {//执行过程中监听
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    	//这个里面细节比较多,暂时不讨论
                }
 });

以上就是属性动画的简单使用。

动画初始化

从 offFloat开始

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setFloatValues(values);//关键方法
        return anim;
    }

主要是初始化动画,然后设置value,这个value就是后面使用的关键节点。

 @Override
    public void setFloatValues(float... 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.ofFloat(mProperty, values));
            } else {//
                setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
            }
        } else {
            super.setFloatValues(values);
        }
    }

第一次进来,mValues肯定是空的,然后我们上面初始化,初始化的是mPropertyName,所以mProperty是空。所以这里记住了setValue只有一个参数,也就是mValue长度是1,我这里是,更多的可以自己搞。
继续看PropertyValuesHolder这个。中间省略一些跳转,最后拿到的下面这个。

public FloatPropertyValuesHolder(String propertyName, float... values) {
            super(propertyName);
            setFloatValues(values);
        }

setFloatValues的参数就是我们传入的0.0,200.0
然后到这里

 public void setFloatValues(float... values) {
        mValueType = float.class;
        mKeyframes = KeyframeSet.ofFloat(values);
    }

KeyframeSet.看名字就是关键帧合集,动画的关键帧就是开始和结束。
这里是FloatKeyframeSet 开始和结束两个关键帧.因为我们就传入两个。。。。

FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];//最小是2.
 if (numKeyframes == 1) {//参数长度
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
            keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
            if (Float.isNaN(values[0])) {
                badValue = true;
            }
        } else {
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
                keyframes[i] =
                        (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
                if (Float.isNaN(values[i])) {
                    badValue = true;
                }
            }
        }

下面处理,如果只传入一个,则开始默认为0.所以上面可以只传入一个200.不过推荐是都传入这样比较明确,不然自己到时候后忘记。当然也可以传入更多。

以上,动画初始化完成。记住几个关键的类:ObjectAnimatorFloatPropertyValuesHolderFloatKeyframeSet

其他几个方法暂时不用看了,直接看动画开始。

动画开始

属性动画的开始,以.start();开始.真正的开始在
ValueAnimation.java
private void start(boolean playBackwards) {

      AnimationHandler.getInstance().autoCancelBasedOn(this); 
     。。。。。。
      addAnimationCallback(0);
      。。。。。
         mAnimationEndRequested = false;
        initAnimation();
        mRunning = true;
        if (mSeekFraction >= 0) {
            mOverallFraction = mSeekFraction;
        } else {
            mOverallFraction = 0f;
        }
        if (mListeners != null) {
            notifyStartListeners();
        }

上面是开始方法的执行过程,有几次跳转,列出来部分重要的方法。这里不分析开始状态的跳转,就是0的时候调整。
开始分析流程:addAnimationCallback(0);这个就是加入执行序列:

//ValueAnimation.java
      getAnimationHandler().addAnimationFrameCallback(this, delay);
//AnimationHandler.java
  public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
        if (mAnimationCallbacks.size() == 0) {
            getProvider().postFrameCallback(mFrameCallback);
        }
        if (!mAnimationCallbacks.contains(callback)) {
            mAnimationCallbacks.add(callback);
        }

        if (delay > 0) {
            mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
        }
    }

上面就是Choreographer.getInstance();添加没一帧的回调。Choreographer这个类,有时间单独再讲,基本可以理解会绘制信号,16ms一次。最后就是到ValueAnimation:: public final boolean doAnimationFrame(long frameTime) 里面处理每一帧动画应该如何处理。这个放到下一节讲。

继续看initAnimation(),这里就是初始化动画。这里不要直接跳转。我们是从ObjectAnimator开始的,先找ObjectAnimator里面有没有这个方法:

 void initAnimation() {
        if (!mInitialized) {
            // mValueType may change due to setter/getter setup; do this before calling super.init(),
            // which uses mValueType to set up the default type evaluator.
            final Object target = getTarget();
            if (target != null) {
                final int numValues = mValues.length;
                for (int i = 0; i < numValues; ++i) {
                    mValues[i].setupSetterAndGetter(target);
                }
            }
            super.initAnimation();
        }
    }

mValues[i].setupSetterAndGetter(target); 这里。我们上面说个value只有一个,就是FloatPropertyValuesHolder,去看看它做了什么
这里不贴代码了,实现实在PropertyValuesHolder.java里面。FloatPropertyValuesHolder是他的内部类。具体作用就是我们把我们传入的PropertyName反射出他的get/set方法,以上面代码为例就是获取到public void setTranslationX()这个方法。所以属性动画这个地方能使用的PropertyName是有限制的,为啥是那几个:TranslationX,TranslationY,RotationX,RotationY,Rotation,ScaleX,ScaleY,Alpha.具体的可以去看view类。

以上,开始初始化完成,主要就是添加回调,和初始化get/set方法。

动画执行

上面说到,动画的执行是在ValueAnimation:: public final boolean doAnimationFrame(long frameTime)

.....
 final long currentTime = Math.max(frameTime, mStartTime);
        boolean finished = animateBasedOnTime(currentTime);

        if (finished) {
            endAnimation();
        }
        .....

一堆判断,我们就看处理动画执行的部分。主要是animateBasedOnTime(),这里执行到最后都是到animateValue()。还是先去找ObjectAnimator.java

 void animateValue(float fraction) {
        final Object target = getTarget();
        if (mTarget != null && target == null) {
            // We lost the target reference, cancel and clean up. Note: we allow null target if the
            /// target has never been set.
            cancel();
            return;
        }

        super.animateValue(fraction);
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].setAnimatedValue(target);
        }
    }

先执行parent的方法 super.animateValue(fraction);

  void animateValue(float fraction) {
        fraction = mInterpolator.getInterpolation(fraction);
        mCurrentFraction = fraction;
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].calculateValue(fraction);
        }
        if (mUpdateListeners != null) {
            int numListeners = mUpdateListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                mUpdateListeners.get(i).onAnimationUpdate(this);
            }
        }
    }

这个有两个关键点:

  1. mValues[i].calculateValue(fraction);
  2. mUpdateListeners.get(i).onAnimationUpdate(this);
    第一个就是计算当前动画执行的节点,第二个就是动画更新的回调。
    我们看下calculateValue(fraction); value还是FloatPropertyValuesHolder。
 @Override
        void calculateValue(float fraction) {
            mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
        }

mFloatKeyframes就是上面说到的FloatKeyframeSet。
这部分代码大家自己,就是取关键点,小于0,取0,大于1取1.中间态根据传入的计算数值来说。
至此,parent执行完毕,回调自身执行。

   int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].setAnimatedValue(target);
        }

这里设置,就是把上面计算的值,设置到target上面,方法就是调用初始化反射出来的set方法。具体get方法干啥用,我暂时没有找到,希望大佬给的提示。谢谢

至此,动画执行大概就是这样,从0到1.

动画结束

动画的结束判断也是在:animateBasedOnTime

 final long scaledDuration = getScaledDuration();
            final float fraction = scaledDuration > 0 ?
                    (float)(currentTime - mStartTime) / scaledDuration : 1f;
            final float lastFraction = mOverallFraction;
            final boolean newIteration = (int) fraction > (int) lastFraction;
            final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
                    (mRepeatCount != INFINITE);
····
float currentIterationFraction = getCurrentIterationFraction(
                    mOverallFraction, mReversing);
                    //看到这里就知道.setRepeatMode(ValueAnimator.REVERSE);//重复模式,反转
····

这里就是判断一次是否执行完成,然后是否设置重复次数。以及重复模式。
然后回调

public final boolean doAnimationFrame(long frameTime) {
····
if (finished) {
            endAnimation();
        }
 ····
 }
 endAnimation(){
 ····
 removeAnimationCallback();
 ····
 for (int i = 0; i < numListeners; ++i) {
                tmpListeners.get(i).onAnimationEnd(this, mReversing);
 }
 ····
 }

removeAnimationCallback,清除callback,Choreographer里面的,以及通知动画结束。
至此,属性动画的基本呢执行流程完成。

这里,看到属性动画只是调用了我们设置的属性的set方法,那么绘制是如何完成的呢?

动画到底是怎么画的

动画的执行,是靠view的刷新机制来完成的。我们去看调用set方法的执行

 public void setTranslationX(float translationX) {
        if (translationX != getTranslationX()) {
            invalidateViewProperty(true, false);
            mRenderNode.setTranslationX(translationX);
            invalidateViewProperty(false, true);

            invalidateParentIfNeededAndWasQuickRejected();
            notifySubtreeAccessibilityStateChangedIfNeeded();
        }
    }

这里最关键的点 mRenderNode.setTranslationX(translationX);
这里就明白为啥属性动画会改变属性了。
看getX方法:

 public float getX() {
        return mLeft + getTranslationX();
    }

然后绘制主要是mRenderNode。这个的移动会造成图片的移动。
因为这部分涉及到native实现,能力有限,没法具体分析,请大家谅解。

据我跟踪java层和查找的部分,可以简单理解,就是硬件加速的图层可以继续使用,所以不会调用view的draw(canvas)方法,但是node移动了,图像也会移动。

这部分,请大佬给我点参考的文章,谢谢!

为什么动画多了会卡顿

因为动画的操作间隔是与view的刷新在一起的,16ms一次。动画多了,16ms里面做的事情太多了,肯定会造成卡顿。
如果一定要执行多个动画,避免卡顿。我们可以:

  1. 自定义执行间隔和差值计算,然后自己在每个间隔里面执行操作view的属性操作。这是一个规避方法。
  2. 动画执行的animateValue只要一个,这样计算只有一个,然后根据这个value值来做多动画的处理,这样也会减少卡顿的产生

以上,结束。请大家指教,谢谢大家。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值