android 属性动画原理分析(一)

前言

一直想研究动画的源码,一直没有时间,终于有时间了,我们通常用的动画主要有帧动画,补间动画以及属性动画,现在常用的就是属性动画,写这篇文章主要为了分析属性动画的实现原理。

首先看一段示例代码,这是一段很常规的X轴从0到600像素的一个平移动画。

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(xiaoguo, "translationX", 0, 600).setDuration(3000);1)
objectAnimator.setInterpolator(new LinearInterpolator());2)
objectAnimator.start();3

(1)ObjectAnimator.ofFloat()方法

这个方法其实就创建出一个ObjectAnimator对象,然后

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
    ObjectAnimator anim = new ObjectAnimator(target, propertyName);
    ------------------------------------------------------------------
    //ObjectAnimator构造中的实现
    1、如果动画已经开始,先取消
    2、将view通过弱引用的形式设置给了mTarget对象
    setTarget(target);  
    setPropertyName(propertyName); //设置mPropertyName属性
    ------------------------------------------------------------------
    anim.setFloatValues(values);
    return anim;
}

(1-1)anim.setFloatValues(values);方法

主要是为了将移动的起始点和终点发封装成一个PropertyValuesHolder对象,这个对象主要封装了入参value的类型,这里是float.class以及动画帧的集合FloatKeyframeSet对象

public void setFloatValues(float... values) {
//刚开始肯定是空的,这个value其实就是FloatPropertyValuesHolder对象数组
    if (mValues == null || mValues.length == 0) {
        if (mProperty != null) {
            setValues(PropertyValuesHolder.ofFloat(mProperty, values));
        } else {
            setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
--------------------------------------------------------------------------------
//通过setValues()方法给mValues和mValuesMap数组进行赋值
            mValues = values;
	        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
	        for (int i = 0; i < numValues; ++i) {
	            PropertyValuesHolder valuesHolder = values[i];
	            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
	        }
--------------------------------------------------------------------------------
        }
    } else {
        super.setFloatValues(values);
    }
}

这个ofFloat方法主要是创建了一个FloatPropertyValuesHolder对象。
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
    return new FloatPropertyValuesHolder(propertyName, values);
}

FloatPropertyValuesHolder构造中调用setFloatValues()方法,将用户设置的values也就是view需要移动到的像素点,封装成了一个FloatKeyframe对象,最终封装到FloatKeyframeSet集合中去保存起来并返回集合。
public void setFloatValues(float... values) {
    mValueType = float.class;
    mKeyframes = KeyframeSet.ofFloat(values);
}

(2)objectAnimator.setInterpolator();方法

这个方法主要用来设置迭代器;所谓迭代器其实就是一个函数,匀速运动就是x,变速的如x^2,等等,高中数学知识。
主要有LinearInterpolator,AccelerateInterpolator、DecelerateInterpolator等等,无非就是匀速,匀加速,匀减速等等,其实就是一个个函数。没啥好讲的。当然你也可以自定义速度的变化函数,都可以。

(3)objectAnimator.start(); 方法

当一系列属性设置完毕之后,接下来就开始启动了,这个方法很长,而且是在父类中实现的,首先是一通赋值;

private void start(boolean playBackwards) {
..............日常判空+给属性赋初始值..................
        addAnimationCallback(0);3-1if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
            startAnimation(); 
            if (mSeekFraction == -1) {
                setCurrentPlayTime(0);3-2} else {
                setCurrentFraction(mSeekFraction);
            }
        }
    }

(3-1)addAnimationCallback(0);方法

1、获取到一个AnimationHandler单例类,是Animator和Choreographer之间的一个桥接类。自身保存callback集合以及延迟的callback集合。
2、AnimationHandler的内部类MyFrameCallbackProvider,用来实例化Choreographer类并注册监听回调,以及获取帧时间操作。

getAnimationHandler().addAnimationFrameCallback(this, delay);
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
    if (mAnimationCallbacks.size() == 0) {
        getProvider().postFrameCallback(mFrameCallback); (1)
    }
    if (!mAnimationCallbacks.contains(callback)) {
        mAnimationCallbacks.add(callback);  (2)	
    }
    if (delay > 0) {
        mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
    }
}

(3-1-1)postFrameCallback()方法

这个方法是用来注册帧回调的,一般来说Choreographer会手机频率100frp的帧率回调doFram方法,通过首先第一个getProvider()方法获取到MyFrameCallbackProvider对象,通过其中的postFrameCallback()方法间接的操作Choreography这个类。

public void postFrameCallback(Choreographer.FrameCallback callback) {
    mChoreographer.postFrameCallback(callback);
    -----------------------------
    //调用Choreographer中的postFrameCallbackDelayed方法
    -------> postFrameCallbackDelayed(callback, 0);
    //入参CALLBACK_ANIMATION:很关键,表示动画,callback是回调函数
    -------> postCallbackDelayedInternal(CALLBACK_ANIMATION,callback, FRAME_CALLBACK_TOKEN, delayMillis);
}

这个方法其实就是通过CALLBACK_ANIMATION从callbackQueue中选出一个CallbackRecord双向队列,然后将回调action添加进去,入队结束,就可以等待帧的回调执行了。
//callbackType:回调类型,属于CALLBACK_ANIMATION,动画类型
//action:动作,回调接口Choreographer.FrameCallback接口,用来执行doFram方法
//token:token值
//delayMillis:延迟时间
private void postCallbackDelayedInternal(int callbackType,Object action, Object token, long delayMillis) {
    synchronized (mLock) {
        final long now = SystemClock.uptimeMillis();
        final long dueTime = now + delayMillis;
//这个方法其实就是一个双向链表CallbackRecorder的一个根据dueTime时间插入操作,值越小约靠前
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
//入队结束,就开始调用scheduleFrameLocked执行
        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);
        }
    }
}

(3-1-2)mAnimationCallbacks

这是一个AnimationCallback的回调,
这里就是简单的添加操作,没有延迟的添加到mAnimationCallbacks数组中,有延迟的添加到mDelayedCallbackStartTime数组中。

(3-2)setCurrentPlayTime(0);方法

这个方法就是设置当前执行的动画的时间,通过插值器不断的计算并得到一个fraction,同时不断的设置这个时间片,来达到动画移动的目的;

public void setCurrentPlayTime(long playTime) {
    float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;
    setCurrentFraction(fraction);
    ------------>animateValue(currentIterationFraction); 
}
这个方法主要做两件事:
1、用给过来的fraction通过插值器来计算当前的fraction。
2、用fraction值,通过估值器来计算当前帧应该到达的像素位置,并赋值给holder中的属性值
void animateValue(float fraction) {
    fraction = mInterpolator.getInterpolation(fraction);1)
    mCurrentFraction = fraction;
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        mValues[i].calculateValue(fraction);2}
    if (mUpdateListeners != null) {
        int numListeners = mUpdateListeners.size();
        for (int i = 0; i < numListeners; ++i) {
            mUpdateListeners.get(i).onAnimationUpdate(this);
        }
    }
}

执行

在(3-1)节中我们设定了framcallback的一个返回帧回调,getProvider().postFrameCallback(mFrameCallback);其中的mFrameCallback是一个回调很出,通过Choreographer不断的回调,从而不断的执行doAnimationFram()方法;

private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        doAnimationFrame(getProvider().getFrameTime());1if (mAnimationCallbacks.size() > 0) {
            getProvider().postFrameCallback(this);2}
    }
};

(1)doAnimationFrame();方法

这个doAnimationFrame方法经过一系列的回调,最终根据传入的时间执行了animateValue方法,就是(3-2)小节是一样的了。

private void doAnimationFrame(long frameTime) {
............................
    callback.doAnimationFrame(frameTime);
    -------->boolean finished = animateBasedOnTime(currentTime);
    -------->animateValue(currentIterationFraction);
............................
}

总结:

动画原理其实最主要的就是通过插值器和估值器,当通过AnimationHandler的内部类MyFramCallback来初始化Choreographer对象,并设置监听,底层将不断返回时间(Choreographer的像素刷新频率是60prf,也就是16ms刷新一帧数据),根据返回的时间,通过插值器计算出一个faction,然后通过估值器进一步计算出当前应该位移到的像素位置,然后刷新界面,将空间设置到当前的像素位置。

暂时分析那么多,等深如了解Choreographer的工作原理以理垂直同步信号后再来补充后续内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值