android属性动画分析

来源:互联网 发布:手机卡iphone7在线软件 编辑:程序博客网 时间:2024/06/05 22:53

        上班太忙,一直想写个博客去记录,各种没动力,万事开头难,今天终于克服重重阻力来写第一篇博客。

        动画一直是个坑,入坑深似海,用法相信都用过,由于属性动画API改动比较多,就选个API25的来分析下,其它版本类似。

        究竟里面的源码究竟是怎样的?怎么去看?首先要猜想,如果你是谷歌的开发人员,要开发类似的功能,要怎么去做?

属性动画,顾名思义就是不断改变属性的值,达到动画的效果。

ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "translationX", 0f, 1000f);

         这是属性动画的常规用法,就是把控件从x方向移动,0移动到1000,第二个参数是属性值,看过View的源码的朋友可能会知道,View里面有个方法是setTranslationX,也是改变x方向的移动的,那它们之间是否有联系?为什么我填"translationX"就能往x方向移动,写"translationY"就往y的方式移动,字符串是怎么被识别到的?最大的可能就是反射。点进去ofFloat方法看看

    

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

        然后点进去ObjectAnimator构造方法看看

          

private ObjectAnimator(Object target, String propertyName) {        setTarget(target);        setPropertyName(propertyName);    }
      是个私有的构造方法,然后调用了两个set方法,我的猜想应该是设置属性的,分别点进去看看,首先是setTarget方法

         

public void setTarget(@Nullable Object target) {        final Object oldTarget = getTarget();        if (oldTarget != target) {            if (isStarted()) {                cancel();            }            mTarget = target == null ? null : new WeakReference<Object>(target);            // New target should cause re-initialization prior to starting            mInitialized = false;        }    }
       可以看到view在这个方法里被赋值给mTarget里,所以先记住mTarget就是我们的View,还是个弱引用,防止内存泄漏。
public void setPropertyName(@NonNull String propertyName) {        // mValues could be null if this is being constructed piecemeal. Just record the        // propertyName to be used later when setValues() is called if so.        if (mValues != null) {            PropertyValuesHolder valuesHolder = mValues[0];            String oldName = valuesHolder.getPropertyName();            valuesHolder.setPropertyName(propertyName);            mValuesMap.remove(oldName);            mValuesMap.put(propertyName, valuesHolder);        }        mPropertyName = propertyName;        // New property/values/target should cause re-initialization prior to starting        mInitialized = false;    }

      这个法也是进行赋值的 ,关键在mPropertyName = propertyName。

   所以ofFloat还调用了setFloatValues方法,

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);        }    }
   前面if(mValues ==null || ==0) 那个方法先不用看,那是特殊情况的处理,先看重点,调用了父类的setFloatValues方法,点进去看看

 

public void setFloatValues(float... values) {        if (values == null || values.length == 0) {            return;        }        if (mValues == null || mValues.length == 0) {            setValues(PropertyValuesHolder.ofFloat("", values));        } else {            PropertyValuesHolder valuesHolder = mValues[0];            valuesHolder.setFloatValues(values);        }        // New property/values/target should cause re-initialization prior to starting        mInitialized = false;    }

    还是看重点,重点在else里面的代码块,发现有个PropertyValuesHolder类,这个类也是做动画常用的类,然后调用了它的setFloatValues方法

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

   mValueType从名字上可以知道是value的类型,这个成员变量属于类类型,这个类里面还有setIntValues,就是类型不同,关键是KeyframeSet.ofFloat(values)

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

  这里明显可以看出,是在遍历这个values数组把value赋值到keyframe数组里面,还有计算了百分比,然后new 了FloatKeyframeSet,把keyframes传进入,最后调用了这个方法

public KeyframeSet(Keyframe... keyframes) {        mNumKeyframes = keyframes.length;        // immutable list        mKeyframes = Arrays.asList(keyframes);        mFirstKeyframe = keyframes[0];        mLastKeyframe = keyframes[mNumKeyframes - 1];        mInterpolator = mLastKeyframe.getInterpolator();    }
 这个方法主要是吧keyframes转成了数组,存在mKeyframes方法里面。

所以来个阶段总结,ObjectAnimator.ofFloat调用了之后做了这几个事情,主要都是赋值,把view设置到mTarget,把propertName设置到mPropertyName里面,把values存到mKeyframes里面,mKeyframes就是属性动画的关键帧。

要调用start()方法才会开启动画,所以我们先看一下start()方法里面做了什么。点进去可以看到,还调用了父类的start()方法,

private void start(boolean playBackwards) {        if (Looper.myLooper() == null) {            throw new AndroidRuntimeException("Animators may only be run on Looper threads");        }        mReversing = playBackwards;        // 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;        mLastFrameTime = 0;        AnimationHandler animationHandler = AnimationHandler.getInstance();        animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));        if (mStartDelay == 0 || mSeekFraction >= 0) {               startAnimation();            if (mSeekFraction == -1) {                               setCurrentPlayTime(0);            } else {                setCurrentFraction(mSeekFraction);            }        }    }
代码很长,其他看不懂先跳过,主要看这个方法调用了startAnimation()方法,然后startAnimation()方法调用了initAnimation()方法,然后常理是点进去initAnimation()方法,这里要注意了,很容易晕,点进去的是ValueAnimator的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();        }    }
 关键代码来了,是mValues[i].setupSetterAndGetter(target),mValues是PropertyValuesHolder的引用,target是view,所以我们进去PropertyValuesHolder查看这个方法。setupSetterAndGetter很长,就不帖代码了,主要里面调用了setupSetter方法,setupSetter调用了setupSetterOrGetter方法,点进去setupSetterOrGetter看看

private Method setupSetterOrGetter(Class targetClass,            HashMap<Class, HashMap<String, Method>> propertyMapMap,            String prefix, Class valueType) {        Method setterOrGetter = null;        synchronized(propertyMapMap) {            HashMap<String, Method> 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<String, Method>();                    propertyMapMap.put(targetClass, propertyMap);                }                propertyMap.put(mPropertyName, setterOrGetter);            }        }        return setterOrGetter;    }

这里有个参数字符串prefix,值是"set",代码里面调用了getPropertyFunction方法,我们点进去看看
 private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {        // TODO: faster implementation...        Method returnVal = null;        String methodName = getMethodName(prefix, mPropertyName);        Class args[] = null;        if (valueType == null) {            try {                returnVal = targetClass.getMethod(methodName, args);            } catch (NoSuchMethodException e) {                // Swallow the error, log it later            }        } else {            args = new Class[1];            Class typeVariants[];            if (valueType.equals(Float.class)) {                typeVariants = FLOAT_VARIANTS;            } else if (valueType.equals(Integer.class)) {                typeVariants = INTEGER_VARIANTS;            } else if (valueType.equals(Double.class)) {                typeVariants = DOUBLE_VARIANTS;            } else {                typeVariants = new Class[1];                typeVariants[0] = valueType;            }            for (Class typeVariant : typeVariants) {                args[0] = typeVariant;                try {                    returnVal = targetClass.getMethod(methodName, args);                    if (mConverter == null) {                        // change the value type to suit                        mValueType = typeVariant;                    }                    return returnVal;                } catch (NoSuchMethodException e) {                    // Swallow the error and keep trying other variants                }            }            // If we got here, then no appropriate function was found        }        if (returnVal == null) {            Log.w("PropertyValuesHolder", "Method " +                    getMethodName(prefix, mPropertyName) + "() with type " + valueType +                    " not found on target class " + targetClass);        }        return returnVal;    }

这里明显看到是用到了反射把set和propertName拼成字符串,并把propertName的首字母改成大写,那就印证了我们一开始的猜想,比如传的是propertName是translationX,就会反射调用了view 的setTranslationX方法,并改变属性。所以调用完initAnimation()方法之后,主要是为PropertyValuesHolder类的mSetter变量赋值,然后再回到ValueAnimator的start()方法里,这里有两行关键代码

 

AnimationHandler animationHandler = AnimationHandler.getInstance(); animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));

点进addAnimationFrameCallback里
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));        }    }

然后着重看getProvider().postFrameCallback(mFrameCallback)方法,点进去发现是个接口方法,首先我们看看getProvide()的具体类是什么
private AnimationFrameCallbackProvider getProvider() {        if (mProvider == null) {            mProvider = new MyFrameCallbackProvider();        }        return mProvider;    }

具体类是MyFrameCallbackProvider,点进这个类查看postFrameCallback()方法的具体实现,然后发现是调用了Choreographer的postFrameCallback()方法。层层调用,最终是调用了Choreographer的postCallbackDelayedInternal()方法
private void postCallbackDelayedInternal(int callbackType,            Object action, Object token, long delayMillis) {        if (DEBUG_FRAMES) {            Log.d(TAG, "PostCallback: type=" + callbackType                    + ", action=" + action + ", token=" + token                    + ", delayMillis=" + delayMillis);        }        synchronized (mLock) {            final long now = SystemClock.uptimeMillis();            final long dueTime = now + delayMillis;            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);            if (dueTime <= now) {                scheduleFrameLocked(now);            } else {                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);                msg.arg1 = callbackType;                msg.setAsynchronous(true);                mHandler.sendMessageAtTime(msg, dueTime);            }        }    }
里面有个判断,如果没设置动画延后时间,就会调用scheduleFrameLocked(now),这个方法里面
private void scheduleFrameLocked(long now) {        if (!mFrameScheduled) {            mFrameScheduled = true;            if (USE_VSYNC) {                if (DEBUG_FRAMES) {                    Log.d(TAG, "Scheduling next frame on vsync.");                }                // If running on the Looper thread, then schedule the vsync immediately,                // otherwise post a message to schedule the vsync from the UI thread                // as soon as possible.                if (isRunningOnLooperThreadLocked()) {                    scheduleVsyncLocked();                } else {                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);                    msg.setAsynchronous(true);                    mHandler.sendMessageAtFrontOfQueue(msg);                }            } else {                final long nextFrameTime = Math.max(                        mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);                if (DEBUG_FRAMES) {                    Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");                }                Message msg = mHandler.obtainMessage(MSG_DO_FRAME);                msg.setAsynchronous(true);                mHandler.sendMessageAtTime(msg, nextFrameTime);            }        }    }
其中 if (isRunningOnLooperThreadLocked()) {
                    scheduleVsyncLocked();
                } else {
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtFrontOfQueue(msg);
                }
判断是否在UI线程执行,在UI线程就调用了Handler的消息通知。最终调用的是原生方法,从底层去绘制,属性动画的主线流程原理大概就是这样,由于篇幅问题,还有很多方法没细讲。有不明白的朋友可以和我交流一下,互相学习


原创粉丝点击