动画(四)属性动画的工作原理
来源:互联网 发布:淘宝价格走势app 编辑:程序博客网 时间:2024/06/10 18:36
前言
本文为Android动画系列的最后一篇文章,通过对源码的分析,能够让大家更深刻地理解属性动画的工作原理,这有助于我们更好地使用属性动画。但是,由于动画的底层实现已经深入到jni层,并且涉及到显示子系统,因此,深入地分析动画的底层实现不仅比较困难而且意义不大,因此,本文的分析到jni层为止。
Android动画系列:
android动画简介
Android动画进阶—使用开源动画库nineoldandroids
Android属性动画深入分析:让你成为动画牛人
Android源码分析—属性动画的工作原理
属性动画的原理
属性动画要求动画作用的对象提供该属性的set方法,属性动画根据你传递的该熟悉的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值都不一样,确切来说是随着时间的推移,所传递的值越来越接近最终值。如果动画的时候没有传递初始值,那么还要提供get方法,因为系统要去拿属性的初始值。对于属性动画来说,其动画过程中所做的就是这么多,下面看源码分析。
源码分析
首先我们要找一个入口,就从ObjectAnimator.ofInt(mButton, width, 500).setDuration(5000).start()开始吧,其他动画都是类似的。
看ObjectAnimator的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
@Override
public
void
start() {
// See if any of the current active/pending animators need to be canceled
AnimationHandler handler = sAnimationHandler.get();
if
(handler !=
null
) {
int
numAnims = handler.mAnimations.size();
for
(
int
i = numAnims -
1
; i >=
0
; i--) {
if
(handler.mAnimations.get(i)
instanceof
ObjectAnimator) {
ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i);
if
(anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
anim.cancel();
}
}
}
numAnims = handler.mPendingAnimations.size();
for
(
int
i = numAnims -
1
; i >=
0
; i--) {
if
(handler.mPendingAnimations.get(i)
instanceof
ObjectAnimator) {
ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i);
if
(anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
anim.cancel();
}
}
}
numAnims = handler.mDelayedAnims.size();
for
(
int
i = numAnims -
1
; i >=
0
; i--) {
if
(handler.mDelayedAnims.get(i)
instanceof
ObjectAnimator) {
ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i);
if
(anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
anim.cancel();
}
}
}
}
if
(DBG) {
Log.d(ObjectAnimator, Anim target, duration: + mTarget + , + getDuration());
for
(
int
i =
0
; i < mValues.length; ++i) {
PropertyValuesHolder pvh = mValues[i];
ArrayList<keyframe> keyframes = pvh.mKeyframeSet.mKeyframes;
Log.d(ObjectAnimator, Values[ + i + ]: +
pvh.getPropertyName() + , + keyframes.get(
0
).getValue() + , +
keyframes.get(pvh.mKeyframeSet.mNumKeyframes -
1
).getValue());
}
}
super
.start();
}</keyframe>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private
void
start(
boolean
playBackwards) {
if
(Looper.myLooper() ==
null
) {
throw
new
AndroidRuntimeException(Animators may only be run on Looper threads);
}
mPlayingBackwards = playBackwards;
mCurrentIteration =
0
;
mPlayingState = STOPPED;
mStarted =
true
;
mStartedDelay =
false
;
mPaused =
false
;
AnimationHandler animationHandler = getOrCreateAnimationHandler();
animationHandler.mPendingAnimations.add(
this
);
if
(mStartDelay ==
0
) {
// This sets the initial value of the animation, prior to actually starting it running
setCurrentPlayTime(
0
);
mPlayingState = STOPPED;
mRunning =
true
;
notifyStartListeners();
}
animationHandler.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
final
boolean
doAnimationFrame(
long
frameTime) {
if
(mPlayingState == STOPPED) {
mPlayingState = RUNNING;
if
(mSeekTime <
0
) {
mStartTime = frameTime;
}
else
{
mStartTime = frameTime - mSeekTime;
// Now that we're playing, reset the seek time
mSeekTime = -
1
;
}
}
if
(mPaused) {
if
(mPauseTime <
0
) {
mPauseTime = frameTime;
}
return
false
;
}
else
if
(mResumed) {
mResumed =
false
;
if
(mPauseTime >
0
) {
// Offset by the duration that the animation was paused
mStartTime += (frameTime - mPauseTime);
}
}
// 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);
return
animationFrame(currentTime);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
);
}
}
}
get方法:在初始化的时候,如果属性的初始值没有提供,则get方法将会被调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private
void
setupValue(Object target, Keyframe kf) {
if
(mProperty !=
null
) {
kf.setValue(mProperty.get(target));
}
try
{
if
(mGetter ==
null
) {
Class targetClass = target.getClass();
setupGetter(targetClass);
if
(mGetter ==
null
) {
// Already logged the error - just return to avoid NPE
return
;
}
}
kf.setValue(mGetter.invoke(target));
}
catch
(InvocationTargetException e) {
Log.e(PropertyValuesHolder, e.toString());
}
catch
(IllegalAccessException e) {
Log.e(PropertyValuesHolder, e.toString());
}
}
set方法:当动画的下一帧到来的时候,PropertyValuesHolder中的setAnimatedValue方法会将新的属性值设置给对象,调用其set方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void
setAnimatedValue(Object target) {
if
(mProperty !=
null
) {
mProperty.set(target, getAnimatedValue());
}
if
(mSetter !=
null
) {
try
{
mTmpValueArray[
0
] = getAnimatedValue();
mSetter.invoke(target, mTmpValueArray);
}
catch
(InvocationTargetException e) {
Log.e(PropertyValuesHolder, e.toString());
}
catch
(IllegalAccessException e) {
Log.e(PropertyValuesHolder, e.toString());
}
}
}
总结
我觉得这篇源码分析写的逻辑有点混乱,希望不要给大家带来误导。从源码上来说,属性动画的源码逻辑层次有点跳跃,不过没关系,大家只要了解属性动画的工作原理就好,源码的作用在于让我们发现其工作原理的确如此。到此为止,Android动画系列已经全部完成,十分感谢大家阅读,希望能给大家带来一点帮助! 0 0
- 动画(四)属性动画的工作原理
- 属性动画的工作原理
- 动画(六)属性动画的工作原理
- 动画四 Android源码分析—属性动画的工作原理
- Android属性动画工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- 动画(五)属性动画的实现原理
- Android动画之属性动画(四)
- 属性动画是如何工作的?
- 属性动画 原理
- 计算机CPU的工作原理动画
- 表达式转型注意事项,和final修饰的变量会被JVM优化
- Deep Learning及NLP(自然语言处理)杂谈--第一部分
- css中margin-left与left的区别
- Deep Learning及NLP(自然语言处理)杂谈--第二部分
- 指针数组与数组指针
- 动画(四)属性动画的工作原理
- Android View 使用自定义stateListDrawable时使用 verifyDrawable(...)
- Java关键字final、static使用总结
- 界面升级: 全新的Dungeons & Dragons 在线商店
- Deep Learning及NLP(自然语言处理)杂谈--第三部分
- lftp连接异常情况分析过程
- linux下c程序的编辑编译运行调试
- [UVA 11865]Stream My Contest[最小树形图][二分答案]
- 二维数组与二维指针(测试环境VS2015)