我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android动画(四)动画框架源码分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android动画(四)动画框架源码分析

本篇难度较大,慎入

也许可以先去看总结在来一起分析
从我们写的开始进入:

 fun click(view: View) {
        val textView = findViewById(R.id.tv)
        val animator = ObjectAnimator.ofFloat(textView,"scale", 0f, 1f)
        animator.duration = 3000
        animator.interpolator = LinearInterpolator()
        animator.start()
    }

首先第三行:

val animator = ObjectAnimator.ofFloat()

调用ofFloat()静态方法初始化一个ObjectAnimator对象,进去:

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

第二行创建对象,没多大讲法
第三行,设值,点进去看

@Override
    public void setFloatValues(float... values) {
        if (mValues == null || mValues.length == 0) {//values没有设置
            // 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);
        }
    }

发现做了一些判断,按上面我们自己写的方法,就会调用

setValues(PropertyValuesHolder.ofFloat(mProperty, values))

首先方法里边实例化PropertyValuesHolder
PropertyValuesHolder.ofFloat(mProperty, values)

所以点进去看来到PropertyValuesHolder这个类下:

public static PropertyValuesHolder ofFloat(Property property, float... values) {
        return new FloatPropertyValuesHolder(property, values);
    }

调用了内部静态类FloatPropertyValuesHolder的构造方法:(FloatPropertyValuesHolder不仅是PropertyValuesHolder的静态内部类,它还继承了PropertyValuesHolder)

public FloatPropertyValuesHolder(Property property, float... values) {
            super(property);
            setFloatValues(values);
            if (property instanceof  FloatProperty) {
                mFloatProperty = (FloatProperty) mProperty;
            }
        }

第三行调用

setFloatValues(values)

@Override
        public void setFloatValues(float... values) {
            super.setFloatValues(values);
            mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
        }

第二行,调用父类也就是PropertyValuesHolder的

setFloatValues()
方法:

PropertyValuesHolder类下的:
public void setFloatValues(float... values) {
        mValueType = float.class;
        mKeyframes = KeyframeSet.ofFloat(values);
    }

在第二行做了一个很关键的事情

mKeyframes = KeyframeSet.ofFloat(values);

点进去来到keyFrameSet的:

public static KeyframeSet ofFloat(float... values) {
        boolean badValue = false;
        int numKeyframes = values.length;
        FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,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;
                }
            }
        }
        if (badValue) {
            Log.w("Animator", "Bad value (NaN) in float animator");
        }
        return new FloatKeyframeSet(keyframes);
    }

把我们传进来的可变参数封装成帧数组

FloatKeyframe keyframes[]
,只有1个参数则它会帮你设置起始帧。封装成数组后在最后调用了
FloatKeyframeSet(keyframes);
实例化FloatKeyframeSet并返回。
回到了PropertyValuesHolder设置到全局变量
mKeyframes = KeyframeSet.ofFloat(values);

然后又设置到了FloatPropertyValuesHolder下全局变量
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;

以上总的来说就是

PropertyValuesHolder.ofFloat(mProperty, values)
执行后把参数们设置到了FloatPropertyValuesHolder下然后封装成帧数组返回

然后

setValues(PropertyValuesHolder.ofFloat(mProperty, values));

看下setValues()干了什么

public void setValues(PropertyValuesHolder... values) {
        int numValues = values.length;
        mValues = values;
        mValuesMap = new HashMap(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;
    }

到这我们的

ObjectAnimator.ofFloat(textView,"scale", 0f, 1f)
这句话就完毕了,并拿到ObjectAnimator对象

然后我们自己设置了插值器啊,动画执行时间啊,没啥说的,都是设置操作,

然后我们调用

ObjectAnimator.start()
开启动画方法,关键来了啊:
点进去来到

 @Override
    public void start() {
        AnimationHandler.getInstance().autoCancelBasedOn(this);//如果之前还在动画运行就取消掉之前动画
        if (DBG) {
            Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration());
            for (int i = 0; i < mValues.length; ++i) {
                PropertyValuesHolder pvh = mValues[i];
                Log.d(LOG_TAG, "   Values[" + i + "]: " +
                    pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " +
                    pvh.mKeyframes.getValue(1));
            }
        }
        super.start();
    }

调用了

super.start()
方法,一看就是父类ValueAnimator的start()方法

private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        mReversing = playBackwards;
        mSelfPulse = !mSuppressSelfPulseRequested;
        // Special case: reversing from seek-to-0 should act as if not seeked at all.
        if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
            if (mRepeatCount == INFINITE) {
                // Calculate the fraction of the current iteration.
                float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
                mSeekFraction = 1 - fraction;
            } else {
                mSeekFraction = 1 + mRepeatCount - mSeekFraction;
            }
        }
        mStarted = true;
        mPaused = false;
        mRunning = false;
        mAnimationEndRequested = false;
        // Resets mLastFrameTime when start() is called, so that if the animation was running,
        // calling start() would put the animation in the
        // started-but-not-yet-reached-the-first-frame phase.
        mLastFrameTime = -1;
        mFirstFrameTime = -1;
        mStartTime = -1;
        addAnimationCallback(0);
        if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
            // If there's no start delay, init the animation and notify start listeners right away
            // to be consistent with the previous behavior. Otherwise, postpone this until the first
            // frame after the start delay.
            startAnimation();
            if (mSeekFraction == -1) {
                // No seek, start at play time 0. Note that the reason we are not using fraction 0
                // is because for animations with 0 duration, we want to be consistent with pre-N
                // behavior: skip to the final value immediately.
                setCurrentPlayTime(0);
            } else {
                setCurrentFraction(mSeekFraction);
            }
        }
    }

重要看27行调用了

addAnimationCallback(0);

private void addAnimationCallback(long delay) {
        if (!mSelfPulse) {
            return;
        }
        getAnimationHandler().addAnimationFrameCallback(this, delay);
    }

getAnimationHandler():

public AnimationHandler getAnimationHandler() {
        return AnimationHandler.getInstance();
    }

拿到对象后后调用了

AnimationHandleraddAnimationFrameCallback()
方法,传进了本类对象,和执行延迟时间:

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));
        }
    }

postFrameCallback从方法名知道,提交了个callback回调对象,也就是说会有其他方法进行调用这个callback回调对象里的函数,什么?这个callback居然就是我们的ValueAnimator,
没错:

public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback {

ValueAnimator 实现了AnimationHandler.AnimationFrameCallback这个回调类!并实现了会被别人回调的关键方法:

 public final boolean doAnimationFrame(long frameTime) {
        if (mStartTime  0) {
                // Offset by the duration that the animation was paused
                mStartTime += (frameTime - mPauseTime);
            }
        }
        if (!mRunning) {
            // If not running, that means the animation is in the start delay phase of a forward
            // running animation. In the case of reversing, we want to run start delay in the end.
            if (mStartTime > frameTime && mSeekFraction == -1) {
                // This is when no seek fraction is set during start delay. If developers change the
                // seek fraction during the delay, animation will start from the seeked position
                // right away.
                return false;
            } else {
                // If mRunning is not set by now, that means non-zero start delay,
                // no seeking, not reversing. At this point, start delay has passed.
                mRunning = true;
                startAnimation();
            }
        }
        if (mLastFrameTime = 0) {
                long seekTime = (long) (getScaledDuration() * mSeekFraction);
                mStartTime = frameTime - seekTime;
                mSeekFraction = -1;
            }
            mStartTimeCommitted = false; // allow start time to be compensated for jank
        }
        mLastFrameTime = frameTime;
        // The frame time might be before the start time during the first frame of
        // an animation.  The "current time" must always be on or after the start
        // time to avoid animating frames at negative time intervals.  In practice, this
        // is very rare and only happens when seeking backwards.
        final long currentTime = Math.max(frameTime, mStartTime);
        boolean finished = animateBasedOnTime(currentTime);
        if (finished) {
            endAnimation();
        }
        return finished;
    }

doAnimationFrame()非常关键的一个方法,由android的Vsnyc进行每16毫秒进行调用一次
这个函数做了一系列动画生命周期的东西
最重要的是调用了

boolean finished = animateBasedOnTime(currentTime);

boolean animateBasedOnTime(long currentTime) {
        boolean done = false;
        if (mRunning) {
            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);
            if (scaledDuration == 0) {
                // 0 duration animator, ignore the repeat count and skip to the end
                done = true;
            } else if (newIteration && !lastIterationFinished) {
                // Time to repeat
                if (mListeners != null) {
                    int numListeners = mListeners.size();
                    for (int i = 0; i < numListeners; ++i) {
                        mListeners.get(i).onAnimationRepeat(this);
                    }
                }
            } else if (lastIterationFinished) {
                done = true;
            }
            mOverallFraction = clampFraction(fraction);
            float currentIterationFraction = getCurrentIterationFraction(
                    mOverallFraction, mReversing);
            animateValue(currentIterationFraction);
        }
        return done;
    }

在第5行算出动画执行百分比,也就是每一帧都会不一样
第19行回调重复的监听
在下一行animateValue(currentIterationFraction);,这个animateValue(currentIterationFraction)方法子类也有实现,所以肯定会先调用子类的
看看子类的执行了什么

 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);
        }
    }

第九行又去调用父类的

animateValue()
方法

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);//调用我们写的监听器,把当前对象传进去
            }
        }
    }

第二行利用传进来的百分比调用插值器获取动画速率
第三行 赋值给成员变量
第五行 还记得吗mValues就是哪个FloatPropertyValuesHolder对象同时也是继承于
PropertyValuesHolder对象,所以去看里面的calculateValue()方法

void calculateValue(float fraction) {
        Object value = mKeyframes.getValue(fraction);
        mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
    }

mKeyframes是我们之前设置的帧集合,调用getValue()
我们找啊找来到FloatKeyframeset下的getValue():

@Override
    public float getFloatValue(float fraction) {
        if (fraction = 1f) {
            final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2);
            final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 1);
            float prevValue = prevKeyframe.getFloatValue();
            float nextValue = nextKeyframe.getFloatValue();
            float prevFraction = prevKeyframe.getFraction();
            float nextFraction = nextKeyframe.getFraction();
            final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
            if (interpolator != null) {
                fraction = interpolator.getInterpolation(fraction);
            }
            float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
            return mEvaluator == null ?
                    prevValue + intervalFraction * (nextValue - prevValue) :
                    ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                            floatValue();
        }
        FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
        for (int i = 1; i < mNumKeyframes; ++i) {
            FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
            if (fraction < nextKeyframe.getFraction()) {
                final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
                float intervalFraction = (fraction - prevKeyframe.getFraction()) /
                    (nextKeyframe.getFraction() - prevKeyframe.getFraction());
                float prevValue = prevKeyframe.getFloatValue();
                float nextValue = nextKeyframe.getFloatValue();
                // Apply interpolator on the proportional duration.
                if (interpolator != null) {
                    intervalFraction = interpolator.getInterpolation(intervalFraction);
                }
                return mEvaluator == null ?
                        prevValue + intervalFraction * (nextValue - prevValue) :
                        ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                            floatValue();
            }
            prevKeyframe = nextKeyframe;
        }
        // shouldn't get here
        return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
    }

就是用fration和插值器和估值器算Vaule值啦,有没有成就感?
返回后在calculateValue里被赋值到了全局变量mAnimatedValue
我们去看看我们平常用的getAnimatedValue()方法把:

Object getAnimatedValue() {
        return mAnimatedValue;
    }


不过这还没完
这只是super.animatedValue()的调用完成
继续看下面

@Override
    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);
        }
    }

有个循环不断进行

mValues[i].setAnimatedValue(target);

这就是如果我们由target控件,就自行帮我们设值!
去看看,它咱们拿到咱们要设置的属性吧:点击来到PropertyValueHolder下的:

void setAnimatedValue(Object target) {
            if (mIntProperty != null) {
                mIntProperty.setValue(target, mIntAnimatedValue);
                return;
            }
            if (mProperty != null) {
                mProperty.set(target, mIntAnimatedValue);
                return;
            }
            if (mJniSetter != 0) {
                nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
                return;
            }
            if (mSetter != null) {
                try {
                    mTmpValueArray[0] = mIntAnimatedValue;//获取之前全局变量mAnimatedValue
                    mSetter.invoke(target, mTmpValueArray);
                } catch (InvocationTargetException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                } catch (IllegalAccessException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                }
            }
        }

这楼里mSetter一般程序没有错误的话都有值,那这个值在哪设置的呢?
我们先跳出上面的过程
回到start()方法来:这是ValueAnimator下的

private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        mReversing = playBackwards;
        mSelfPulse = !mSuppressSelfPulseRequested;
        // Special case: reversing from seek-to-0 should act as if not seeked at all.
        if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
            if (mRepeatCount == INFINITE) {
                // Calculate the fraction of the current iteration.
                float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
                mSeekFraction = 1 - fraction;
            } else {
                mSeekFraction = 1 + mRepeatCount - mSeekFraction;
            }
        }
        mStarted = true;
        mPaused = false;
        mRunning = false;
        mAnimationEndRequested = false;
        // Resets mLastFrameTime when start() is called, so that if the animation was running,
        // calling start() would put the animation in the
        // started-but-not-yet-reached-the-first-frame phase.
        mLastFrameTime = -1;
        mFirstFrameTime = -1;
        mStartTime = -1;
        addAnimationCallback(0);
        if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
            // If there's no start delay, init the animation and notify start listeners right away
            // to be consistent with the previous behavior. Otherwise, postpone this until the first
            // frame after the start delay.
            startAnimation();
            if (mSeekFraction == -1) {
                // No seek, start at play time 0. Note that the reason we are not using fraction 0
                // is because for animations with 0 duration, we want to be consistent with pre-N
                // behavior: skip to the final value immediately.
                setCurrentPlayTime(0);
            } else {
                setCurrentFraction(mSeekFraction);
            }
        }
    }

我们发现在倒数11行调用了startAnimation();,问题来了,我们之前 addAnimationCallback(0);不是已经在运行动画了吗?其实从方法名和之前讲的就已经知道了addAnimationCallback就是添加回调,执行还得看android的Vsync的信号,这里注释也说明白了,如果这个动画不是延时动画,在第一帧的时候并不会有效果,第二帧才会有,因为得等startAnimation()获取setter这个东东。为什么,现在来点进去看看吧:

private void startAnimation() {
        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
            Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
                    System.identityHashCode(this));
        }
        mAnimationEndRequested = false;
        initAnimation();
        mRunning = true;
        if (mSeekFraction >= 0) {
            mOverallFraction = mSeekFraction;
        } else {
            mOverallFraction = 0f;
        }
        if (mListeners != null) {
            notifyStartListeners();
        }
    }

主要执行了 initAnimation();这个方法,
我们发现子类ObjectAnimator中还会有 initAnimation()同名方法,毫无疑问先执行子类的咯:

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();
        }
    }

第九行调用PropertyValueHolder的

setupSetterAndGetter(target)
;

@Override
       void setupSetterAndGetter(Object target) {
        if (mProperty != null) {
            // check to make sure that mProperty is on the class of target
            try {
                Object testValue = null;
                List keyframes = mKeyframes.getKeyframes();
                int keyframeCount = keyframes == null ? 0 : keyframes.size();
                for (int i = 0; i < keyframeCount; i++) {
                    Keyframe kf = keyframes.get(i);
                    if (!kf.hasValue() || kf.valueWasSetOnStart()) {
                        if (testValue == null) {
                            testValue = convertBack(mProperty.get(target));
                        }
                        kf.setValue(testValue);
                        kf.setValueWasSetOnStart(true);
                    }
                }
                return;
            } catch (ClassCastException e) {
                Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
                        ") on target object " + target + ". Trying reflection instead");
                mProperty = null;
            }
        }
        // We can't just say 'else' here because the catch statement sets mProperty to null.
        if (mProperty == null) {
            Class targetClass = target.getClass();
            if (mSetter == null) {
                setupSetter(targetClass);
            }
            List keyframes = mKeyframes.getKeyframes();
            int keyframeCount = keyframes == null ? 0 : keyframes.size();
            for (int i = 0; i < keyframeCount; i++) {
                Keyframe kf = keyframes.get(i);
                if (!kf.hasValue() || kf.valueWasSetOnStart()) {
                    if (mGetter == null) {
                        setupGetter(targetClass);
                        if (mGetter == null) {
                            // Already logged the error - just return to avoid NPE
                            return;
                        }
                    }
                    try {
                        Object value = convertBack(mGetter.invoke(target));
                        kf.setValue(value);
                        kf.setValueWasSetOnStart(true);
                    } catch (InvocationTargetException e) {
                        Log.e("PropertyValuesHolder", e.toString());
                    } catch (IllegalAccessException e) {
                        Log.e("PropertyValuesHolder", e.toString());
                    }
                }
            }
        }
    }

第29行mSetter 为空时调用

setupSetter(target.getClass());

void setupSetter(Class targetClass) {
        Class propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
        mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
    }

在第3行

mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);

传入

targetClass,我们的控件类字节码对象 sSetterPropertyMap 这个字节码对象下用于保存所有的set开头函数的Map “set” set方法前面的名字 propertyType 参数类型

进去看

private Method setupSetterOrGetter(Class targetClass,
            HashMap<Class, HashMap> propertyMapMap,
            String prefix, Class valueType) {
        Method setterOrGetter = null;
        synchronized(propertyMapMap) {
            // Have to lock property map prior to reading it, to guard against
            // another thread putting something in there after we've checked it
            // but before we've added an entry to it
            HashMap propertyMap = propertyMapMap.get(targetClass);
            boolean wasInMap = false;
            if (propertyMap != null) {
                wasInMap = propertyMap.containsKey(mPropertyName);
                if (wasInMap) {
                    setterOrGetter = propertyMap.get(mPropertyName);
                }
            }
            if (!wasInMap) {
                setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
                if (propertyMap == null) {
                    propertyMap = new HashMap();
                    propertyMapMap.put(targetClass, propertyMap);
                }
                propertyMap.put(mPropertyName, setterOrGetter);
            }
        }
        return setterOrGetter;
    }

第十八行

setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);

这里边就是反射获取get和set函数
然后返回返回。
mSetter终于有值了呢,回去吧,话说我们之前是从这跳出来的:

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

第十七行就用这个invoke(调用的意思):mSetter.invoke(target, mTmpValueArray);设置控件value了
终于没了呀

总结: ObjectAnimatior.ofFloat()进行一系列初始化,比如KeyframeSet,设置target控件,返回ObjectAnimatior对象。 然后由我们继续设置插值器,估值器啊什么的。 初始化完成后start()开始,初始化回调类然后提交回调,也就是ObjectAnimatior本身,因为实现了回调类。 android会由Vsync信号每隔16ms调用一次这个回调类下的回调方法。 设置好回调后,紧接着就会反射去获取get和set方法。 信号一来,回调方法就会计算当前时间和开始时间这段时间动画的百分比啊。
然后放到KeyFrameSet里和估值器,插值器,动画开始和结束帧,求得当前动画属性值 最后就是用反射获取到的方法进行设值啦
作者:Eliza白


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android动画(四)动画框架源码分析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android动画(四)动画框架源码分析

本篇难度较大,慎入 也许可以先去看总结在来一起分析 从我们写的开始进入:fun click(view: View) {val textView = findViewById(R.id.tv)val animator = ObjectAnim
2022-06-06

Android动画框架,让平移动画更添魅力

使用startAnimation()​方法时,View的位置在动画结束后会重置为原始位置,除非在动画结束时手动更新View的位置。

Android RecyclerView的Item自定义动画及DefaultItemAnimator源码分析

这是关于RecyclerView的第二篇,说的是如何自定义Item动画,但是请注意,本文不包含动画的具体实现方法,只是告诉大家如何去自定义动画,如何去参考源代码。 我们知道,RecyclerView默认会使用DefaultItemAnima
2022-06-06

Android源码解析之属性动画详解

前言 大家在日常开发中离不开动画,属性动画更为强大,我们不仅要知道如何使用,更要知道他的原理。这样,才能得心应手。那么,今天,就从最简单的来说,了解下属性动画的原理。ObjectAnimator.ofInt(mView,"width",10
2022-06-06

Android编程中Tween动画和Frame动画实例分析

本文实例讲述了Android编程中Tween动画和Frame动画实现方法。分享给大家供大家参考,具体如下: Animation主要有两种动画模式:Tween动画和Frame动画 Tween动画由四种类型组成alpha渐变透明度动画效果sca
2022-06-06

Android动画之小球拟合动画的示例分析

这篇文章给大家分享的是有关Android动画之小球拟合动画的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Android动画之小球拟合动画实例实现效果:动画组成:1.通过三阶贝塞尔曲线来拟合圆,拟合系数的
2023-05-31

补间动画源码中分析机制原理

补间动画移动后,点击事件的响应为什么还在原来的位置?那今天我们就来从源码解析原理,我们可以通过平移、旋转、缩放、透明度等API进行具体的操作.

Android转场动画深入分析探究

对于一个动画而言,它是由多个分镜头组成的,而转场就是分镜之间衔接方式。转场的主要目的,就是为了让镜头与镜头之间过渡的更加自然,让动画更加连贯,例如两个页面切换之间的衔接动画
2022-11-13

Android 四种动画效果的调用实现代码

(1) main.xml 代码如下:(声明四个按钮控件) XML代码: 代码如下: 2022-06-06

Android动画了解—一些项目案例分析

文章目录写在前面带路径运动的过渡动画整体缩放的效果整体宽高改变的过渡效果两个界面布局的过渡效果带水波纹布局的效果几行代码实现转圈圈的效果界面翻转的效果 写在前面 由于我前面写了N多篇文章,但是还是感觉缺少一些相关的生动的例子,所以分析一些,
2022-06-06

Android框架之OkHttp3源码的示例分析

这篇文章将为大家详细讲解有关Android框架之OkHttp3源码的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。OkHttp流程图OkHttp基本使用gradle依赖implementation
2023-06-15

Android框架之Volley源码的示例分析

这篇文章主要介绍Android框架之Volley源码的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Volley简单使用我这里是以依赖架包的形式 ,大家也可以以gradle的形式进行依赖。好了,接下来上代码了
2023-06-15

Android实现旋转动画的方式代码分享

这篇文章主要介绍“Android实现旋转动画的方式代码分享”,在日常操作中,相信很多人在Android实现旋转动画的方式代码分享问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Android实现旋转动画的方式代
2023-06-20

HTML+JS实现爱心动画效果的源码分享

最近在热播影剧《点燃我,温暖你》中有个片段是男主人公李峋通过代码实现了一个爱心动画的效果。在本文中,将利用HTML+CSS+JS实现同款效果,需要的可以尝试一下
2022-11-13

Android动画之3D翻转效果实现函数分析

Android中的翻转动画效果的实现,首先看一下运行效果如上图所示. Android中并没有提供直接做3D翻转的动画,所以关于3D翻转的动画效果需要我们自己实现,那么我们首先来分析一下Animation 和 Transformation。
2022-06-06

Android编程中PopupWindow的用法分析【位置、动画、焦点】

本文实例讲述了Android编程中popupwindow用法。分享给大家供大家参考,具体如下: 在Android中有很多级别的Window,不同级别的Window按照z-index方向分布。下面看看Android控件(view)PopupW
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录